2009-10-19 2 views
3

В настоящее время я использую систему таймера/обратного вызова с использованием fastdelegates дона Клагстона. (См http://www.codeproject.com/KB/cpp/FastDelegate.aspx)Передача между указателями функций

Вот исходный код:

struct TimerContext 
{ 
}; 

void free_func(TimerContext*) 
{ 
} 

struct Foo 
{ 
    void member_func(TimerContext*) 
    { 
    } 
}; 

Foo f; 
MulticastDelegate< void (TimerContext*) > delegate; 

delegate += free_func; 
delegate += bind(&Foo::member_func, &f); 

Хорошо, но теперь, я хочу, чтобы пользователь смог подклассы TimerContext хранить и передавать свои собственные структуры для обратных вызовов. Цель здесь заключается в предотвращении пользователя от необходимости в TimerContext обратное приведение себя

struct TimerContext 
{ 
}; 

struct MyTimerContext : TimerContext 
{ 
    int user_value; 
}; 

void free_func(TimerContext*) 
{ 
} 

void free_func2(MyTimerContext*) 
{ 
} 

struct Foo 
{ 
    void member_func(TimerContext*) 
    { 
    } 

    void member_func2(MyTimerContext*) 
    { 
    } 
}; 

Foo f; 
MulticastDelegate< void (TimerContext*) > delegate; 

delegate += free_func; 
delegate += free_func2; 
delegate += bind(&Foo::member_func, &f); 
delegate += bind(&Foo::member_func2, &f); 

Как вы уже догадались, GCC не позволит мне сделать это :)

error: invalid conversion from `void (*)(MyTimerContext*)' to `void (*)(TimerContext*)' 
error: initializing argument 1 of `delegate::Delegate<R()(Param1)>::Delegate(R (*)(Param1)) [with R = void, Param1 = TimerContext*]' 

Так что теперь мой вопрос: Если я заставляю актерский состав reinterpret_cast, он будет работать, но будет ли он безопасен?

PS: Это время критически важные функции обратного вызова, тяжелые виртуальные-ориентированные решения считаются неосуществимо:/

ответ

5

C++ стандарт говорит в 13.4/7, что:

нет никаких стандартных преобразований (пункт 4) одного типа указатель-функция в другой. В частности, даже если B является публичной базой D, мы имеем

D* f(); 
B* (*p1)() = &f; // error 
void g(D*); 
void (*p2)(B*) = &g; // error 

Тем не менее, вы можете быть может использовать функцию адаптера для хранения указателей на функцию с одним аргументом, что-то вроде boost::function, но я не верный прямо сейчас, решит ли он вашу проблему.

+0

Howw, ладно :(Спасибо за ссылку! – NewbiZ

+0

Даже повышение :: функция не будет работать в его случае, потому что TimerContext * не неявно конвертируются в MyTimerContext *. Я упомянул этот вопрос типа безопасности в моем ответ, но я почему-то отказался от него. – sellibitze

0

Конечно, указатели функций каста, как правило, плохая идея.

Может ли работать указатель функции от void(*)(Derived*) до void(*)(Base*) или он может не работать. Это, конечно, не будет работать, если внутреннее представление указателей Derived * и Base * необходимо скорректировать при конвертации. Однако в случае вашего единственного отношения наследования это маловероятно. Тем не менее, макет класса и корректировки указателя определяются реализацией, и вы не должны полагаться на это. Если вы хотите взять на себя риск: Идите ahread.

Предполагая, что указатели не нужно регулировать, преобразование с void(*)(Derived1*) в void(*)(Base*) все равно не будет хорошей идеей (не является безопасным по типу), поскольку оно позволяет использовать функцию, которая ожидает, что Derived1 * будет вызываться с Derived2 *, где Derived1 и Derived2 являются братьями и сестрами в иерархии наследования.

+1

Это не реализация, это неопределенное поведение (что имеет несколько иные последствия). –

+0

Нет, вы неправильно поняли. Макет класса и возможные корректировки указателя определяются реализацией. – sellibitze

0

reinterpret_cast является настолько же безопасным, если «отправитель» и «приемник» объекта соответствуют. Поэтому, если отправитель и получатель реализованы одним и тем же фрагментом кода, он может быть довольно безопасным в течение некоторого времени.

Если вы хотите добавить обратные вызовы делегата, и вы хотите их иметь различный тип, как вы делаете, у вас есть два сценария-х:

  • вы знаете, все делегаты заранее, в compile time => вы можете обернуть их в список типов.

  • делегаты могут быть составлены во время выполнения => вам необходимо использовать привязку во время выполнения, то есть виртуальные функции (метод exec или тому подобное).

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

  • Нет связанных вопросов^_^