PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Funktionspointer im Template Konstruktor


Gast
2007-09-13, 09:16:45
moin

Ich versuche ein Problem mit Templates zu lösen. Dazu habe ich folgenden Rumpf eines Klassen Templates implementiert:
template <class _T>
class TemplateTest
{
public:
inline TemplateTest()
{
};

inline TemplateTest( _T (*foo)(size_t) )
{
};

inline ~TemplateTest(){};

private:
(_T) (*m_pFoo)(size_t);
}; Wenn ich jetzt versuche dies zu erzeugen:
double test(size_t i)
{
return 3.0 * i;
};

int _tmain(int argc, _TCHAR* argv[])
{
TemplateTest<double> ttt;
ttt = TemplateTest<double>( test );
return 0;
}Sagt mit mein Visual Studio
error C2440: 'Typumwandlung': 'double (__cdecl *)(size_t)' kann nicht in 'TemplateTest<_T>' konvertiert werden
with
[
_T=double
]

Mit einen normalen (z.B. double) Parameter funktioniert es hingegen. Nur den Funktionspointer will er nicht richtig übernehmen. Jemand eine Idee, was da falsch läuft? danke

Gast
2007-09-13, 10:29:26
Eine Funktion mit Funktion Pointer funktioniert im Übrigen auch nicht:
inline void set_function_ptr( _T (*foo)(size_t) )
{
};
Hier stört er sich irgendwie an der Signatur - muß man function pointer irgendwie anders deklarieren?

del_4901
2007-09-13, 10:38:51
Probier mal das:
typedef _T (*foo)(size_t);
inline void set_function_ptr(foo name)
{
};
foo ist nämlich nur ein Typ und keine Variable.

MuLuNGuS
2007-09-13, 11:40:12
wie lautet noch die goldene regel, du sollst keinen unterstrich vor deinem GROSSEM 'T' benutzen :D:D:D

hier, viel spaß:


#include <iostream>
#include <TCHAR.H>

using namespace std;

template <class T>
class TemplateTest
{
public:
inline TemplateTest()
{
}

inline TemplateTest( T (*foo)(size_t) )
{
m_pFoo = foo;
}

inline ~TemplateTest(){}


void printvalue()
{
cout << m_pFoo(123) << endl;
}

private:
(T) (*m_pFoo)(size_t);
};

double test(size_t i)
{
return 3.0f * i;
};

int _tmain(int argc, _TCHAR* argv[])
{
TemplateTest<double> ttt(test);

ttt.printvalue();



return 0;
}

Gast
2007-09-13, 12:37:20
Super! Was es nicht alles gibt ... das Beispiel woran ich mich orientiert hatte, verwendete aber auch _T :)

Wieso geht das in der ursprünglichen Form nicht?

MuLuNGuS
2007-09-13, 13:51:15
nun,

ein kurzer blick ans ende der TCHAR.H bringt folgendes zu tage. ;-)


/* Generic text macros to be used with string literals and character constants.
Will also allow symbolic constants that resolve to same. */

#define _T(x) __T(x)
#define _TEXT(x) __T(x)


#ifdef __cplusplus
} /* ... extern "C" */
#endif

#endif /* _INC_TCHAR */

Gast
2007-09-13, 14:05:15
Danke! :)

Gast
2007-09-14, 09:59:52
Ich wundere mich noch über etwas...

Mein Template funktioniert jetzt prinzipiell so wie es soll, allerdings kommt es etwas auf den Funktionspointer an.

Das Ganze funktioniert nicht mehr, sobald der Funktionspointer keine globale Funktion oder static Funktion ist.

Ich versuche dem Konstruktor oder der Funktion set_function_ptr() einen Funktionspointer auf eine Member-Funktion exakt passender Signatur und Rückgabetyp der gleichen klasse zeigen zu lassen, in der auch eine Instanz der Template Klase ist. Dies funktioniert allerdings nicht:
error C2664: 'TemplateTest<T,I>::set_function_ptr': Konvertierung des Parameters 1 von 'double (size_t)' in 'double (__cdecl *)(size_t)' nicht möglich
with
[
T=double,
I=100
]

Wenn ich die Funktion aus dem Objekt rausziehe oder zu einer static Methode mache, funktioniert es - das will ich aber nicht. Was macht einen Pointer auf eine Memberfunktion hier anders?

malte.c
2007-09-14, 11:28:15
Super! Was es nicht alles gibt ... das Beispiel woran ich mich orientiert hatte, verwendete aber auch _T :)

Wieso geht das in der ursprünglichen Form nicht?

Unterstrich-Großbuchstabe und doppelter Unterstrich am Anfang sind für den Compiler bzw. die Standardbibliothek reserviert. Du darfst prinzipiell weder Macros noch Variablen noch sonst etwas so nennen.

malte.c
2007-09-14, 11:36:28
Das Ganze funktioniert nicht mehr, sobald der Funktionspointer keine globale Funktion oder static Funktion ist. [...] Was macht einen Pointer auf eine Memberfunktion hier anders?

Memberfunktionen können nicht ohne Kontext aufgerufen werden. Funktionen und statische Memberfunktionen können durch einen einfachen Pointer dargestellt werden, wohingegen Du je nach Compiler bei Memberfunktionen noch mindestens einen Pointer auf die Instanz der Klasse brauchst. Vergleiche einfach mal mit sizeof() die Größen.

Wenn Du Dich näher für sowas interessierst, kannst Du zum einen mal einen Blick auf die Boost-Function-Object-Bibliotheken werfen, http://www.boost.org/libs/libraries.htm#Function-objects . Für Template-Geschichten bietet sich aber auch http://www.amazon.de/Templates-Complete-Guide-David-Vandervoorde/dp/0201734842/ an.

Gast
2007-09-14, 13:38:27
Danke! Hmm, ich habe jetzt nochmal gesucht und auch Infos gefunden, daß man für einen Pointer auf eine Memberfunktion (PTMF?) immer irgendwie die Klasse in der Signatur angeben muß double (TestKlasse::* PTMF)(size_t).

Bedeutet das umgekehrt, daß man einer Klasse A keinen Pointer auf eine Funktion einer Instanz von Klasse B zum Ausführen geben kann, (auch wenn die Signatur ansonsten stimmt) ohne Klasse A so zu modifizieren, daß die Signatur zur Übergabe des Funktionspointers etwas der Form trägt double (B::* foo)(size_t)? :(

Gast
2007-09-14, 14:22:16
Ich dachte ich bin jetzt ganz schlau und wenn im Topic schon "Template" steht, dann bediene ich mich auch dessen. Ich habe unten stehendes Template so erweitert.


template <typename T, typename U>
class TemplateTest
{
public:
typedef T (U::* PTMF)(size_t);

inline TemplateTest()
{
};

inline TemplateTest( PTMF foo )
{
m_pFoo = foo;
};

inline ~TemplateTest(){};

inline void check(size_t i)
{
m_pFoo(i);
};

private:
PTMF m_pFoo;
}; Wenn ich jetzt in einer anderen Klasse B ein Objekt mit TemplateTest<double,B> ttt = TemplateTest<double,B>( someMemberFunctionWithCorrectSignatureOfB ); erzeuge, dann funktioniert das, was vorher nicht funktioniert hat. D.h. er nimmt die Adresse und baut ein Objekt.

Wenn ich allerdings dann darauf zugreifen will mit ttt.check(1); kommt ein Compile Fehler der Sorte
error C2064: Ausdruck ergibt keine Funktion, die 1 Argumente übernimmt

Was läuft denn hier falsch? :( Oder geht das auch irgendwie ohne diese Templatemodifikation?

del_4901
2007-09-14, 17:57:10
Ich dachte ich bin jetzt ganz schlau und wenn im Topic schon "Template" steht, dann bediene ich mich auch dessen. Ich habe unten stehendes Template so erweitert.


template <typename T, typename U>
class TemplateTest
{
public:
typedef T (U::* PTMF)(size_t);

inline TemplateTest()
{
};

inline TemplateTest( PTMF foo )
{
m_pFoo = foo;
};

inline ~TemplateTest(){};

inline void check(size_t i)
{
m_pFoo(i);
};

private:
PTMF m_pFoo;
}; Wenn ich jetzt in einer anderen Klasse B ein Objekt mit TemplateTest<double,B> ttt = TemplateTest<double,B>( someMemberFunctionWithCorrectSignatureOfB ); erzeuge, dann funktioniert das, was vorher nicht funktioniert hat. D.h. er nimmt die Adresse und baut ein Objekt.

Wenn ich allerdings dann darauf zugreifen will mit ttt.check(1); kommt ein Compile Fehler der Sorte
error C2064: Ausdruck ergibt keine Funktion, die 1 Argumente übernimmt

Was läuft denn hier falsch? :( Oder geht das auch irgendwie ohne diese Templatemodifikation?
Ohne Objekt kann keine Memberfunktion aufgerufen werden. Entweder man passt die Signatur an (this-Pointer am Anfang). Oder man nutzt die Pointer2Member Operatoren (.* , ->*).

Gast
2007-09-17, 08:03:17
Danke! Aber ich verstehe noch nicht so ganz, was dies bedeutet? Muß ich zusätzlich einen Pointer auf das Objekt speichern wo der Funktionspointer herkommt oder wie funktioniert das? Bzw. wo braucht ich das?

Und wie funktioniert das Pointer2Member Operator - das kannte ich so noch nicht; wird damit die Funktion irgendwie "dereferenziert"?

del_4901
2007-09-17, 10:07:38
Danke! Aber ich verstehe noch nicht so ganz, was dies bedeutet? Muß ich zusätzlich einen Pointer auf das Objekt speichern wo der Funktionspointer herkommt oder wie funktioniert das? Bzw. wo braucht ich das?

Und wie funktioniert das Pointer2Member Operator - das kannte ich so noch nicht; wird damit die Funktion irgendwie "dereferenziert"?

Ja, du brauchst den this-Pointer also ein Objekt, mit dem kannst du dann auch eine Methode aufrufen.

objekt_ptr->*Methode_ptr(Parameter);
objekt_ref.*Methode_ptr(Parameter);

oder du castest

typedef T (U::* PTMF)(size_t);

auf

typedef T (*PTMF2)(U*, size_t);

del_4901
2007-09-17, 11:18:40
Ich würde das ja ganz Anders machen:


template <typename R>
class TemplateTest
{
public:
typedef R (*PTMF)(void* , size_t);

inline TemplateTest(){}
inline ~TemplateTest(){}

template<class T, R (T::*Method)(size_t)>
inline static TemplateTest<R> bind(void* object)
{
return manufacture(object, &gen_func<T, Method>);
}

inline R invoke(size_t value)
{
return method(object, value);
}

private:
void* object;
PTMF method;

template <class T, R (T::*Method)(size_t)>
inline static R gen_func(void* object, size_t value)
{
T* self = static_cast<T*>(object);
return (self->*Method)(value);
}

inline static TemplateTest<R> manufacture(void* object, PTMF method)
{
TemplateTest<R> ret;
ret.object = object;
ret.method = method;
return ret;
}

};



struct MyBase
{
void blah(size_t i)
{
std::cout << i << std::endl;
}
};


MyBase base;

TemplateTest<void> bla;
bla = bla.bind<MyBase, &MyBase::blah>(&base);
bla.invoke(1);

Gast
2007-09-19, 08:55:57
Vielen Dank auf jeden Fall! Muß sagen, ich verstehe noch nicht ganz wieso das so funktionieren muß, aber auf jeden Fall funktioniert dies was ich machen wollte endlich. Und in absehbarer Zeit, verstehe ich bestimmt auch die ganzen Zusammenhänge von Funktionspointer auf Memberfunktionen... danke

del_4901
2007-09-19, 09:39:25
Das ist eigentlich ganz simpel. Anstatt der Methode wird eine statische Funktion aufgerufen. Self ist hierbei der Ersatz für den this-Pointer. Wenn der Compiler nicht ganz dumm ist, kann er aber den ganzen Kladeradatsch "wegoptimieren".

Gast
2007-09-21, 11:55:00
Danke! Denke ich verstehe schon, warum das funktioniert. Eher noch nicht so ganz, warum dies nicht einfacher funktioniert. D.h. wenn ich auf die Methode einer Funktion in einer existierenden Instanz zeige, müßte man doch an der Adresse was zum Ausführen haben...

Momentan wundere ich mich aber noch über was anderes. Und zwar versuche ich in dem Template ein weiteres Template zu halten - in Form eines STL Vectors.

Das funktioniert auch ganz gut, bis zu dem Zeitpunkt wo ich einen Iterator auf diesen Container zurückgeben möchte - also sowas machen möchte:

inline std::vector<R>::const_iterator begin() const
{
return data.begin();
}

std::vector<R> data;

Was geht daran denn nicht - darf man Template in der form nicht schachteln?

danke

del_4901
2007-09-21, 11:59:33
Was geht daran denn nicht - darf man Template in der form nicht schachteln?

danke
müsste gehen, kann es manchmal sein das R "void" ist?

Xmas
2007-09-21, 16:37:51
Danke! Denke ich verstehe schon, warum das funktioniert. Eher noch nicht so ganz, warum dies nicht einfacher funktioniert. D.h. wenn ich auf die Methode einer Funktion in einer existierenden Instanz zeige, müßte man doch an der Adresse was zum Ausführen haben...
Methoden gehören aber zur Klasse, es hat nicht jede Instanz ihre eigenen Methoden. Um eine nicht-statische Methode aufzurufen brauchst du nicht nur die Methode selbst (oder deren Adresse), sondern auch eine Instanz auf der die Methode operieren soll. Mit einem Pointer alleine funktioniert es nicht.

del_4901
2007-09-21, 16:39:59
Methoden gehören aber zur Klasse, es hat nicht jede Instanz ihre eigenen Methoden. Um eine nicht-statische Methode aufzurufen brauchst du nicht nur die Methode selbst (oder deren Adresse), sondern auch eine Instanz auf der die Methode operieren soll. Mit einem Pointer alleine funktioniert es nicht.
Doch man sollte es mit reinterpretcast casten können, ich kenne aber keinen Compiler, der das richtig implementiert. (Ich muss auch sagen das ich in letzter Zeit nur mit MSC gearbeitet habe.)

Xmas
2007-09-21, 16:41:19
Gast, probier es mal mit
inline typename std::vector<R>::const_iterator begin() const
Der Compiler weiß sonst nicht dass das von R abhängige Symbol const_iterator ein Datentyp ist.

Doch man sollte es mit Referencecast casten können, ich kenne aber keinen Compiler, der das richtig implementiert. (Ich muss auch sagen das ich in letzter Zeit nur mit MSC gearbeitet habe.)
Ein Reference Cast ist doch nur eine Unterart von dynamic_cast. Und was genau willst du überhaupt casten?

del_4901
2007-09-21, 16:51:01
Ein Reference Cast ist doch nur eine Unterart von dynamic_cast. Und was genau willst du überhaupt casten?
Natürlich reinterpret_cast, das ist mir jetzt auch aufgefallen, sooft nimmt man den ja nicht. Einfach ein this vor die Parameterliste packen und casten.

Xmas
2007-09-21, 17:00:04
Err, ja, aber du brauchst doch trotzdem Instanz und Methode. Und nicht, wie der Gast meinte, einen Pointer auf eine Methode einer Instanz.

del_4901
2007-09-21, 17:01:03
Err, ja, aber du brauchst doch trotzdem Instanz und Methode. Und nicht, wie der Gast meinte, einen Pointer auf eine Methode einer Instanz.
Ja klar, den this-Zeiger braucht man immer. Mehr ist es ja nicht. Aber einige (alle) Compiler sind an der Stelle kompliziert, und da geht das mit dem reinterpret_cast nicht.