2012-01-21 3 views
3

Рассмотрим следующий код:внешне определяется указатель на функцию в качестве аргумента шаблона

main.cpp:

#include <iostream> 
typedef void (* fncptr)(void); 
extern void externalfunc(void); 

template<void(* test)(void)> 
class Bar 
{ 
public: 
    Bar() { test(); } 
}; 

void localfunc() 
{ 
    std::cout << "Hello World" << std::endl; 
} 
constexpr fncptr alias = localfunc; 
extern fncptr externalAlias; 
int main(int argc, char* argv[]) 
{ 
    Bar<localfunc> b; 
    Bar<alias> b2; // This wouldn't compile if alias wasn't constexpr 
    Bar<externalfunc> b3; 
// Bar<externalAlias> b4; 

    return 0; 
} 

и external.cpp:

#include <iostream> 

typedef void (* fncptr)(void); 

void externalfunc() 
{ 
    std::cout << "Hello external world" << std::endl; 
} 

fncptr externalAlias = externalfunc; 

Теперь проблема мне нужно что-то вроде 4-я строка в основной функции для работы. Я получаю эти объявления функций из внешней библиотеки C, поэтому я не могу их трогать. В настоящее время 4-я строка не компилируется. gcc 4.6 говорит, что «это должен быть адрес функции с внешней связью». На самом деле это также говорит об этом, если вы делаете alias не constexpr, поэтому фактическое значение (я думаю) должно интерпретироваться как «Я не знаю, на 100% уверен, что адрес функции, который вы мне даете, постоянный, который мне нужен для создания экземпляра этого шаблона ». Есть ли способ обойти это, так как я не могу объявить externalalias как constexpr в main.cpp?

Прежде чем вы придете с альтернативными решениями: я уже пытаюсь получить эту работу, просто передав указатели на функцию через конструктор и сохраните их локально, но мне все же интересно, могу ли я заставить рабочую версию шаблона работать :).

+1

Как компилятор должен знать, что такое значение 'externalAlias', чтобы он мог создать экземпляр шаблона? –

+0

Да, он не знает этого значения 'externalAlias', но это переменная и, следовательно, он знает расположение этой переменной во время компиляции, поэтому она может создавать шаблон. (как видно в другом ответе (т. е. мой ответ :-))). –

+0

Почему вы хотите сделать специализацию класса с помощью шаблона? Для этого и есть наследование. (т. е. test() должен быть просто виртуальным методом Bar), а затем иметь реализации теста в производных классах, которые соответствуют localfunc, alias, externfunc и т. д. Или, еще лучше, Bar - это просто отдельный класс и передается экземпляр класса и «ITest» в его конструкторе. Выполнение сложных штук с помощью шаблонов приводит к тому, что недопустимый код для других наследует. – selbie

ответ

2

Имена alias и externAlias: не функции! Они являются указателями на функции и как таковые изменяемы. Вы не можете использовать изменяемые объекты в качестве аргументов шаблона, потому что аргументы шаблона должны быть разрешены во время компиляции. Однако есть две вещи, которые вы можете сделать:

  1. Вы можете создать оболочку, вызывающую потенциально сглаженную функцию, используя любое имя, которое вы хотите предоставить оболочке. Это также может удобно регулировать тип, если существует некоторый уровень разницы между сигнатурами функций.
  2. Вы можете использовать указатель на указатель функции в качестве аргумента шаблона: здесь также применяется обычный еще один уровень предположения о косвенности.

Все это говорит о том, чего вы на самом деле пытаетесь достичь? Вы уверены, что не хотите что-то вроде std::function<void(void)>? Я понимаю, что это используется для стирания типа, в то время как у вас, кажется, есть противоположная цель, и превращайте указатели функций в уникальные типы. Однако, как вы это делаете, кажется, что это не то, что вы намереваетесь делать.

Вот пример того, как второй вариант может выглядеть следующим образом:

#include <iostream> 

extern void f(int); 
extern void g(int); 

void (*falias)(int) = f; 
void (*galias)(int) = g; 

template <void (**alias)(int)> 
struct foo 
{ 
    void bar() { (*alias)(17); } 
}; 

void f(int x) { std::cout << "f(x)=" << x << "\n"; } 
void g(int x) { std::cout << "g(x)=" << x << "\n"; } 

int main() 
{ 
    foo<&falias>().bar(); 
    foo<&galias>().bar(); 
} 

Тот факт, что f() и g() в конечном итоге реализуется в тех же единицах перевода совершенно несущественно: вы можете переместить их в другое место без какие-то проблемы.

+0

Я использую библиотеку, которая имеет определенный callFlow, такой как «createX, doSomethingWithX, deleteX», но с X различными для разных целей. Поскольку логика, окружающая эти вызовы, одинакова для всех применений, я попытался поместить это в один шаблонный контейнер и передайте эти 3 указателя функций на этот шаблон, уменьшая необходимость скопировать эту функциональность для каждого X. Тип класса должен быть уникальным для каждого X также (из-за некоторой статики). – KillianDS

+0

Почему вы не можете использовать '& createX',' & doSomethingWithX ',' & deleteX' в этом случае? Кажется, это функции в библиотеке. Если они не являются, но могут быть псевдонимами (т.е. вызваны через указатель на функцию), вам придется создать какую-то оболочку. ... или использовать опцию 2. –

+0

Я попробовал это сделать как ваш пример, и он работал для 'localfunc' моего исходного примера, но не' externalfunc'. Но я, вероятно, собираюсь перейти к опции 1 и добавить другую оболочку :). – KillianDS

1

Если вы готовы вручную немного аннотировать типы, это возможно. В конечном счете, типы различных объектов тонко отличаются.

Я пишу этот ответ назад, глядя на main первый:

int main(int argc, char* argv[]) 
{ 
    Bar<void(*)(void) , localfunc> b; 
    Bar<void(**)(void), &alias> b2; 
    Bar<void(*)(void) , externalfunc> b3; 
    Bar<void(**)(void), &externalAlias> b4; 

    return 0; 
} 

Вы могли бы хотеть рассмотреть typeof (г ++) или decltype (C++ 11), чтобы создать немного более удобным для чтения, и, возможно, использовать макрос:

Bar<decltype(&localfunc), localfunc> b; 
Bar<decltype(&alias), &alias> b2; 
Bar<decltype(&externalfunc), externalfunc> b3; 
Bar<decltype(&externalAlias), &externalAlias> b4; 

Я должен был изменить шаблон Бар немного:

template<typename T, T t> 
class Bar; 

, так что он может иметь дело с указателями функций и с указателями -to-function-указатели. Для этого требуется две специализации:

template<void(* test)(void)> 
class Bar<void(*)(void), test> 
{ 
public: 
    Bar() { std::cout << "* "; test(); } 
}; 
template<void(** test)(void)> 
class Bar<void(**)(void), test> 
{ 
public: 
    Bar() { std::cout << "**"; (*test)(); } 
}; 

Это должно быть возможно добавить дополнительные специализации для других типов функций с различными типами параметров и типы возвращаемых, в том числе даже при вызове метода.

Я проверил это полностью на своей машине. Вот демо (без внешней функции) на ideone.

Примечание: с помощью псевдонимов параметр шаблона является адресом псевдонима, и, следовательно, он учитывает любые изменения, которые вы вносите в переменную псевдонима во время выполнения. Думаю, вы, вероятно, захотите сделать псевдонимы const. const fncptr alias = localfunc;

Смежные вопросы