Нубо-шаблоно-программист здесь.
Есть что-то похожее на делегат:
class IDelegate { public: virtual void Call() = 0; protected: IDelegate( ){} virtual ~IDelegate( ){} }; template<typename T, typename P1> class TypeDelegate : public IDelegate { typedef void ( T::*FuncPtr)( P1); public: TypeDelegate( T * pObj, FuncPtr pFuncPtr, P1 iParam) : m_pObject( pObj), m_pFunc( pFuncPtr), m_iParam( iParam) { } void Call( ) { ( m_pObject->*m_pFunc)( m_iParam); } private: T * m_pObject; FuncPtr m_pFunc; P1 m_iParam; };
Использование:
class TestClass { public: void func(int n) { } }; void main( ) { TestClass * pTest = new TestClass( ); int nNum = 10; IDelegate * pD = new TypeDelegate<TestClass, int>( pTest, &TestClass::func, nNum); pD->Call( ); }
Всё работает. Но мне очень сильно хочется избавиться от необходимости указывать типы аргументов для сохраняемой функции
IDelegate * pD = new TypeDelegate<TestClass, int>(pTest, &TestClass::func, nNum);
Как нибудь можно от них избавиться? Может как-нибудь через boost::any, или RTTI/typeid, или через еще один шаблон?
Это возможно?
0xdeadc0de
template< class T, class F, class Arg> IDelegate* makeDelegate(T *pTest, F func, Arg nNum){ return new TypeDelegate<T, Arg>( pTest, func, nNum); } IDelegate * pD = makeDelegate( pTest, &TestClass::func, nNum);
Try
Не компилится.
Упрощенный вариант:
class TestClass { public: void func(int * pP) { } void func( int n) { } }; template<class T, class F> void makeDelegate( T * pObject, F func) { } void main( ) { TestClass * pTest = new TestClass( ); makeDelegate( pTest, &TestClass::func); }
компилятор говорит:
error C2914: 'makeDelegate' : cannot deduce template argument as function argument is ambiguous
error C2784: 'void makeDelegate(T *,F)' : could not deduce template argument for 'T *' from 'TestClass *'
Почему компилятор не может распознать класс, я понятия не имею. Хотя в других примерах, которые я раскуриваю, такой код работает.
Если задать руками явно:
makeDelegate<TestClass>(pTest, &TestClass::func);
, то финт срабатывает, но на втором аргументе новый затык:
error C2914: 'makeDelegate' : cannot deduce template argument as function argument is ambiguous
error C2784: 'void makeDelegate(T *,F)' : could not deduce template argument for 'overloaded function type' from 'overloaded function type'
see declaration of 'makeDelegate'
Здесь у меня знаний не хватает. Что это, и почему оно не нравится компилятору?
0xdeadc0de
А, понятно. Он затыкается в неизвестность из-за двух функций с одинаковым именем у класса TestClass.
Курю дальше.
#include <functional> #include <iostream> using namespace std; class TestClass { public: void foo() { cout << "Blah" << endl; } }; int main( ) { std::function<void( TestClass*)> func = &TestClass::foo; TestClass tc; func( &tc); return 0; }
Я тоже пытался так велосипедить, благо мне объяснили что к чему:)
А твой делегат лучше выкинь нафиг...
bazhenovc
Мне не подходит такая штука. Как не подходят boost::function, boost::signals и прочие такие же вещи.
Мне нужен не совсем делегат, а нечто, что могло бы сохранить в себе обьект, его функцию, её параметры, и быть "прозрачно"-одинаковым и не зависеть от используемого обьекта, его функции и параметров этой функции.
0xdeadc0de
#include <functional> #include <iostream> using namespace std; class TestClass { public: void foo(int a) { cout << "Blah" << endl; } void bar( ) { cout << "Moo" << endl; } }; template <typename T, typename R, typename ...Args> struct Delegate { T* ptr; std::function<R( T*, Args... args)> func; Delegate( ) {} Delegate( const Delegate& other) { ptr = other.ptr; func = other.func; } void call( Args... args) { if ( func && ptr) func( ptr, std::forward<Args>( args)...); } }; #define MAKE_DELEGATE( X, T, F) {X.ptr = T; X.func = F;} int main( ) { TestClass tc; Delegate<TestClass, void, int> dlg1; MAKE_DELEGATE( dlg1, &tc, &TestClass::foo); dlg1.call( 42); Delegate<TestClass, void> dlg2; MAKE_DELEGATE( dlg2, &tc, &TestClass::bar); dlg2.call( ); return 0; }
Самому подумать лениво? :)
EDIT: А аргументы можно сложить в контейнер с Variant`ами, типа вот такого
bazhenovc
Можно считать, что я работаю с темплейтами первую неделю за всю жизнь. (Учитывая, сколько я уже себе нервов вытрепал, я бы хотел, что бы эта неделя никогда не начиналась)
Что такое "typename ...Args", в частности "..."?
Это не из нового стандарта (гугл подозрительно часто упомянает c++0x)?
Если да - мне не подходит.
0xdeadc0de
Вариадические шаблоны.
Из нового. Можно сделать и без этого, но тогда прийдётся хардкодить делегаты по кол-ву аргументов, что не есть гуд...
0xdeadc0de
#include <iostream> using namespace std; #include "Variant.hh" class TestClass { public: void foo() { cout << "Blah" << endl; } void bar( ) { cout << "Moo" << endl; } }; struct DelegateBase { virtual void call( ) = 0; virtual ~DelegateBase( ) {} Variant obj; Variant func; }; template <typename T, typename F> struct Delegate : public DelegateBase { Delegate( T* object, F function) { obj = object; func = function; } virtual void call( ) { T* self = obj.toValue<T*>( ); auto real_func = func.toValue<F>( ); ( self->*real_func)( ); } }; int main( ) { TestClass tc; DelegateBase* dlg = new Delegate<TestClass, typeof( &TestClass::bar)>( &tc, &TestClass::bar); dlg->call( ); delete dlg; dlg = new Delegate<TestClass, typeof( &TestClass::foo)>( &tc, &TestClass::foo); dlg->call( ); delete dlg; return 0; }
Вот так должно сработать. Если нужны аргументы - можно сделать по аналогии.
bazhenovc
// C++0x, gcc #include <string> #include <iostream> #include <vector> #include <typeinfo> template< class ...Args> class Signal{ public: Signal(){ } Signal( const Signal& s ){ *this = s; } Signal( const Signal && s ){ data = s.data; s.data.clear( ); } ~Signal( ){ disconnectAll( ); } Signal& operator = ( const Signal &s ){ disconnectAll( ); data.resize( s.data.size( ) ); for( decltype( data.size( ) ) i = 0; i<data.size( ); ++i) data[i] = s.data[i]->clone( ); return *this; } void disconnectAll( ){ for( auto i=data.begin( ); i!=data.end( ); ++i) delete ( *i); } template< class T > void connect( T *t, void ( T::*f)( Args... ) ){ disconnect( t, f); data.push_back( new Data<T>( t, f) ); } template< class T > void disconnect( T *t, void ( T::*f)( Args... ) ){ for( decltype( data.size( ) ) i = 0; i<data.size( ); ){ if( data[i]->cmp( typeid( t), typeid( f) ) ){ delete data[i]; data[i] = data.back( ); data.pop_back( ); } else { ++i; } } } void emit( Args... a ){ for( auto i=data.begin( ); i!=data.end( ); ++i) ( *i)->call( a... ); } private: class IData{ public: virtual ~IData( ){} virtual void call( Args... ) = 0; virtual bool cmp( const std::type_info& t, const std::type_info& f ) = 0; virtual IData* clone( ) const = 0; }; template< class T > class Data:public IData{ public: Data( T* t, void ( T::*f)( Args... ) ){ obj = t; func = f; } void call( Args... a ){ ( obj->*func)( a... ); } bool cmp( const std::type_info& t, const std::type_info& f ){ return t==typeid( obj ) && f==typeid( func ); } IData* clone( ) const{ return new Data<T>( obj, func); } T* obj; void ( T::*func)( Args... ); }; std::vector<IData*> data; }; // Использование class B{ public: void foo( int x, float y){ std::cout << "B::foo "<< x<<" " <<y << std::endl; } }; class A{ public: void foo( int x, float y){ std::cout << "A::foo "<< x<<" " <<y << std::endl; } }; Signal<int, float> func( Signal<int, float> foo ){ foo.emit( 3, 3.14); return foo; } int main( ){ A a; B b; Signal<int, float> foo; foo.connect( &b, &B::foo ); foo.connect( &a, &A::foo ); foo.emit( 1, 1.1); foo.disconnect( &b, &B::foo ); foo.emit( 2, 2.2); func( foo).emit( 4, 4.4); return 0; } // Вывод программы B::foo 1 1.1 A::foo 1 1.1 A::foo 2 2.2 A::foo 3 3.14 A::foo 4 4.4
bazhenovc
Variant.hh - это что?
Путем перебора и осмысливания я пришел к выводу, что это не опечатка бустовского variant.hpp, не std-шная вещь, не Поисково-спасательный вертолёт HH-43 Huskie, и не std/variant.d из D.
Try
EDIT:
Теперь сделайSignal<int, float> foo;
foo.connect(
И посмотрим, что будет;)
В любом случае, ТС просил без c++0x...
З.Ы.
Не говоря уже о том, что я терпеть не могу плюсовый RTTI
bazhenovc
А, забыл. Спасибо, раскуриваю.
Тема в архиве.