j'ai un probleme avec la classe cURLpp::Types::WriteFunctionFunctor
cURLpp::Types::WriteFunctionFunctor functor(WriteMemoryCallback);
size_t WriteMemoryCallback(char* ptr, size_t size, size_t nmemb){..};
Ca fonctionne très bien tant que WriteMemoryCallback est une simple
fonction. Par contre, si je l'encapsule dans une classe, GCC renvoie:
[...] erreur: no matching function for call [...]
class ButineurVirtuel {
public:
ButineurVirtuel();
std::string load(std::string url);
~ButineurVirtuel();
private:
*static* size_t WriteMemoryCallback(char*, size_t , size_t );
};
j'ai un probleme avec la classe cURLpp::Types::WriteFunctionFunctor
cURLpp::Types::WriteFunctionFunctor functor(WriteMemoryCallback);
size_t WriteMemoryCallback(char* ptr, size_t size, size_t nmemb){..};
Ca fonctionne très bien tant que WriteMemoryCallback est une simple
fonction. Par contre, si je l'encapsule dans une classe, GCC renvoie:
[...] erreur: no matching function for call [...]
class ButineurVirtuel {
public:
ButineurVirtuel();
std::string load(std::string url);
~ButineurVirtuel();
private:
*static* size_t WriteMemoryCallback(char*, size_t , size_t );
};
j'ai un probleme avec la classe cURLpp::Types::WriteFunctionFunctor
cURLpp::Types::WriteFunctionFunctor functor(WriteMemoryCallback);
size_t WriteMemoryCallback(char* ptr, size_t size, size_t nmemb){..};
Ca fonctionne très bien tant que WriteMemoryCallback est une simple
fonction. Par contre, si je l'encapsule dans une classe, GCC renvoie:
[...] erreur: no matching function for call [...]
class ButineurVirtuel {
public:
ButineurVirtuel();
std::string load(std::string url);
~ButineurVirtuel();
private:
*static* size_t WriteMemoryCallback(char*, size_t , size_t );
};
none wrote on 01/10/2006 22:24:j'ai un probleme avec la classe cURLpp::Types::WriteFunctionFunctor
cURLpp::Types::WriteFunctionFunctor functor(WriteMemoryCallback);
size_t WriteMemoryCallback(char* ptr, size_t size, size_t nmemb){..};
Ca fonctionne très bien tant que WriteMemoryCallback est une simple
fonction. Par contre, si je l'encapsule dans une classe, GCC renvoie:
[...] erreur: no matching function for call [...]
en effet, "l'encapsulation" dans une classe change la visibilité.
class ButineurVirtuel {
public:
ButineurVirtuel();
std::string load(std::string url);
~ButineurVirtuel();
private:
*static* size_t WriteMemoryCallback(char*, size_t , size_t );};
une callback doit être static.
none wrote on 01/10/2006 22:24:
j'ai un probleme avec la classe cURLpp::Types::WriteFunctionFunctor
cURLpp::Types::WriteFunctionFunctor functor(WriteMemoryCallback);
size_t WriteMemoryCallback(char* ptr, size_t size, size_t nmemb){..};
Ca fonctionne très bien tant que WriteMemoryCallback est une simple
fonction. Par contre, si je l'encapsule dans une classe, GCC renvoie:
[...] erreur: no matching function for call [...]
en effet, "l'encapsulation" dans une classe change la visibilité.
class ButineurVirtuel {
public:
ButineurVirtuel();
std::string load(std::string url);
~ButineurVirtuel();
private:
*static* size_t WriteMemoryCallback(char*, size_t , size_t );
};
une callback doit être static.
none wrote on 01/10/2006 22:24:j'ai un probleme avec la classe cURLpp::Types::WriteFunctionFunctor
cURLpp::Types::WriteFunctionFunctor functor(WriteMemoryCallback);
size_t WriteMemoryCallback(char* ptr, size_t size, size_t nmemb){..};
Ca fonctionne très bien tant que WriteMemoryCallback est une simple
fonction. Par contre, si je l'encapsule dans une classe, GCC renvoie:
[...] erreur: no matching function for call [...]
en effet, "l'encapsulation" dans une classe change la visibilité.
class ButineurVirtuel {
public:
ButineurVirtuel();
std::string load(std::string url);
~ButineurVirtuel();
private:
*static* size_t WriteMemoryCallback(char*, size_t , size_t );};
une callback doit être static.
Sylvain wrote:
[...]
une callback doit être static.
Typiquement, il faut aussi qu'elle soit « extern "C" », ce qui
exclut des fonctions membres statiques aussi. Il faut qu'elle
soit globale.
Sylvain wrote:
[...]
une callback doit être static.
Typiquement, il faut aussi qu'elle soit « extern "C" », ce qui
exclut des fonctions membres statiques aussi. Il faut qu'elle
soit globale.
Sylvain wrote:
[...]
une callback doit être static.
Typiquement, il faut aussi qu'elle soit « extern "C" », ce qui
exclut des fonctions membres statiques aussi. Il faut qu'elle
soit globale.
James Kanze a écrit:Sylvain wrote:
[...]une callback doit être static.
Typiquement, il faut aussi qu'elle soit « extern "C" », ce qui
exclut des fonctions membres statiques aussi. Il faut qu'elle
soit globale.
J'ai cru comprendre dans un autre post qu'on pouvait passer
par un typedef 'magique' si on avait vraiment besoin d'une
fonction membre à la fois «static» et «extern "c"».
Je m'explique.
Kevlin Henney (www.curbralan.com), contributeur de boost, a commi:
http://www.two-sdg.demon.co.uk/curbralan/papers/accu/C++Threading.pdf et
http://www.two-sdg.demon.co.uk/curbralan/papers/accu/MoreC++Threading.pdf
J'aime bien cette façon de permettre au compilateur de
vérifier les types jusqu'au bout. Cependant, dans le code
suivant (copié-collé-commenté) :
template<typename nullary_function>
struct return_type
{
typedef typename nullary_function::result_type type;
};
template<typename function_result_type>
struct return_type<function_result_type (*)()>
{
typedef function_result_type type;
};
//...
class threader
{
public:
template<typename threadable>
joiner<return_type<threadable>::type> operator()(threadable function)
{
typedef threaded<threadable> threaded;
thread_t handle;
// ici, «thread_create» attend une fonction «extern "C"», <===
// c'est embêtant car on veut lui passer «threaded::needle».
if(!thread_create( &handle, 0,
threaded::needle,
new threaded(function)))
throw bad_thread();
return joiner<return_type<threadable>::type>(handle);
}
private:
template<typename threadable>
class threaded
{
public:
explicit threaded(threadable main) : main(main)
{
}
static void *needle(void *eye)
{
// ne peut pas être une fonction libre car < ===
// dépendant du paramètre de template.
std::auto_ptr<threaded> that(static_cast<threaded *>(eye));
return new return_type<threadable>::type(that->main());
}
private:
threadable main;
};
};
Ce code est donc illégal et ce serait bien dommage s'il n'y avait
pas de solution.
titre d'exercice, J'ai implémenté «threader»
de cette façon après avoir lu un truc (de toi?) sur c.l.c++.m.
Simplifié ça donne:
extern "C" typedef void* (thread_function_t)(void*);
extern "C" typedef thread_function_t* thread_start_address_t;
class threader
{
public:
template<typename threadable>
joiner<return_type<threadable>::type> operator()(threadable function)
{
typedef threaded<threadable> threaded;
thread_t handle;
if(!thread_create( &handle, 0,
threaded::needle,
new threaded(function)))
throw bad_thread();
return joiner<return_type<threadable>::type>(handle);
}
private:
template<typename threadable>
class threaded
{
public:
explicit threaded(threadable main) : main(main)
{
}
// déclare une fonction membre statique extern "C" ? < ===
static thread_function_t needle;
private:
threadable main;
};
};
// définit une fonction membre statique extern "C" ? < ===
template<typename threadable>
void* threader::threaded<threadable>::needle(void *eye)
{
std::auto_ptr<threaded> that(static_cast<threaded *>(eye));
return new return_type<threadable>::type(that->main());
}
Est-ce legal de cette façon ?
En tout cas ça compile sans un warning avec VC 2005.
James Kanze a écrit:
Sylvain wrote:
[...]
une callback doit être static.
Typiquement, il faut aussi qu'elle soit « extern "C" », ce qui
exclut des fonctions membres statiques aussi. Il faut qu'elle
soit globale.
J'ai cru comprendre dans un autre post qu'on pouvait passer
par un typedef 'magique' si on avait vraiment besoin d'une
fonction membre à la fois «static» et «extern "c"».
Je m'explique.
Kevlin Henney (www.curbralan.com), contributeur de boost, a commi:
http://www.two-sdg.demon.co.uk/curbralan/papers/accu/C++Threading.pdf et
http://www.two-sdg.demon.co.uk/curbralan/papers/accu/MoreC++Threading.pdf
J'aime bien cette façon de permettre au compilateur de
vérifier les types jusqu'au bout. Cependant, dans le code
suivant (copié-collé-commenté) :
template<typename nullary_function>
struct return_type
{
typedef typename nullary_function::result_type type;
};
template<typename function_result_type>
struct return_type<function_result_type (*)()>
{
typedef function_result_type type;
};
//...
class threader
{
public:
template<typename threadable>
joiner<return_type<threadable>::type> operator()(threadable function)
{
typedef threaded<threadable> threaded;
thread_t handle;
// ici, «thread_create» attend une fonction «extern "C"», <===
// c'est embêtant car on veut lui passer «threaded::needle».
if(!thread_create( &handle, 0,
threaded::needle,
new threaded(function)))
throw bad_thread();
return joiner<return_type<threadable>::type>(handle);
}
private:
template<typename threadable>
class threaded
{
public:
explicit threaded(threadable main) : main(main)
{
}
static void *needle(void *eye)
{
// ne peut pas être une fonction libre car < ===
// dépendant du paramètre de template.
std::auto_ptr<threaded> that(static_cast<threaded *>(eye));
return new return_type<threadable>::type(that->main());
}
private:
threadable main;
};
};
Ce code est donc illégal et ce serait bien dommage s'il n'y avait
pas de solution.
titre d'exercice, J'ai implémenté «threader»
de cette façon après avoir lu un truc (de toi?) sur c.l.c++.m.
Simplifié ça donne:
extern "C" typedef void* (thread_function_t)(void*);
extern "C" typedef thread_function_t* thread_start_address_t;
class threader
{
public:
template<typename threadable>
joiner<return_type<threadable>::type> operator()(threadable function)
{
typedef threaded<threadable> threaded;
thread_t handle;
if(!thread_create( &handle, 0,
threaded::needle,
new threaded(function)))
throw bad_thread();
return joiner<return_type<threadable>::type>(handle);
}
private:
template<typename threadable>
class threaded
{
public:
explicit threaded(threadable main) : main(main)
{
}
// déclare une fonction membre statique extern "C" ? < ===
static thread_function_t needle;
private:
threadable main;
};
};
// définit une fonction membre statique extern "C" ? < ===
template<typename threadable>
void* threader::threaded<threadable>::needle(void *eye)
{
std::auto_ptr<threaded> that(static_cast<threaded *>(eye));
return new return_type<threadable>::type(that->main());
}
Est-ce legal de cette façon ?
En tout cas ça compile sans un warning avec VC 2005.
James Kanze a écrit:Sylvain wrote:
[...]une callback doit être static.
Typiquement, il faut aussi qu'elle soit « extern "C" », ce qui
exclut des fonctions membres statiques aussi. Il faut qu'elle
soit globale.
J'ai cru comprendre dans un autre post qu'on pouvait passer
par un typedef 'magique' si on avait vraiment besoin d'une
fonction membre à la fois «static» et «extern "c"».
Je m'explique.
Kevlin Henney (www.curbralan.com), contributeur de boost, a commi:
http://www.two-sdg.demon.co.uk/curbralan/papers/accu/C++Threading.pdf et
http://www.two-sdg.demon.co.uk/curbralan/papers/accu/MoreC++Threading.pdf
J'aime bien cette façon de permettre au compilateur de
vérifier les types jusqu'au bout. Cependant, dans le code
suivant (copié-collé-commenté) :
template<typename nullary_function>
struct return_type
{
typedef typename nullary_function::result_type type;
};
template<typename function_result_type>
struct return_type<function_result_type (*)()>
{
typedef function_result_type type;
};
//...
class threader
{
public:
template<typename threadable>
joiner<return_type<threadable>::type> operator()(threadable function)
{
typedef threaded<threadable> threaded;
thread_t handle;
// ici, «thread_create» attend une fonction «extern "C"», <===
// c'est embêtant car on veut lui passer «threaded::needle».
if(!thread_create( &handle, 0,
threaded::needle,
new threaded(function)))
throw bad_thread();
return joiner<return_type<threadable>::type>(handle);
}
private:
template<typename threadable>
class threaded
{
public:
explicit threaded(threadable main) : main(main)
{
}
static void *needle(void *eye)
{
// ne peut pas être une fonction libre car < ===
// dépendant du paramètre de template.
std::auto_ptr<threaded> that(static_cast<threaded *>(eye));
return new return_type<threadable>::type(that->main());
}
private:
threadable main;
};
};
Ce code est donc illégal et ce serait bien dommage s'il n'y avait
pas de solution.
titre d'exercice, J'ai implémenté «threader»
de cette façon après avoir lu un truc (de toi?) sur c.l.c++.m.
Simplifié ça donne:
extern "C" typedef void* (thread_function_t)(void*);
extern "C" typedef thread_function_t* thread_start_address_t;
class threader
{
public:
template<typename threadable>
joiner<return_type<threadable>::type> operator()(threadable function)
{
typedef threaded<threadable> threaded;
thread_t handle;
if(!thread_create( &handle, 0,
threaded::needle,
new threaded(function)))
throw bad_thread();
return joiner<return_type<threadable>::type>(handle);
}
private:
template<typename threadable>
class threaded
{
public:
explicit threaded(threadable main) : main(main)
{
}
// déclare une fonction membre statique extern "C" ? < ===
static thread_function_t needle;
private:
threadable main;
};
};
// définit une fonction membre statique extern "C" ? < ===
template<typename threadable>
void* threader::threaded<threadable>::needle(void *eye)
{
std::auto_ptr<threaded> that(static_cast<threaded *>(eye));
return new return_type<threadable>::type(that->main());
}
Est-ce legal de cette façon ?
En tout cas ça compile sans un warning avec VC 2005.
Manuel Zaccaria wrote:J'ai cru comprendre dans un autre post qu'on pouvait passer
par un typedef 'magique' si on avait vraiment besoin d'une
fonction membre à la fois «static» et «extern "c"».
Non. Rien qu'à partir du concepte, c'est impossible. Rien ne
dit que les deux langages passent leurs paramètres de la même
façon : le C++ pourrait les passer dans les registres, par
exemple, et le C sur la pile. (Si de telles différences me
semblent peu probables, j'ai déjà vu le cas sur Intel où la
responsibilité du nettoyage de la pile n'était pas la même.) Or,
si celui qui utilise le pointeur s'attend à une fonction C, il
va passer les paramètres comme le veut le C. Et ta fonction C++
va avoir du mal à s'y retrouver.
template<typename nullary_function>
struct return_type
{
typedef typename nullary_function::result_type type;
};
template<typename function_result_type>
struct return_type<function_result_type (*)()>
{
typedef function_result_type type;
};
Il y a d'autres solutions ; regarde, par exemple, comment fait
boost.threads. Sinon, la solution classique, c'est bien
d'utiliser une classe abstraite de base ; il y a bien une
conversion void* en AbstractThread* dans la fonction qu'appelle
le système, mais cette fonction est dans le sous-système des
threads. On assure donc une correction de type vis-à-vis des
utilisateurs.
static void *needle(void *eye)
Elle pourrait être une fonction libre templatée, sauf qu'une
fonction templatée ne peut pas être « extern "C" » non plus.
Mais...
Aussi, en passant, l'utilisation d'auto_ptr ici ne marche pas
avec tous les compilateurs ; le destructeur n'en serait pas
appelé par g++, par exemple, en cas de pthread_cancel.
Je ne vois rien dans ce code qui exige que la fonction soit
membre ; l'utilisation d'une classe abstraite de base fonctionne
aussi bien.
titre d'exercice, J'ai implémenté «threader»
de cette façon après avoir lu un truc (de toi?) sur c.l.c++.m.
Je doute que ce soit de moi. Par hazard, je suis une des rares
(apparamment) personnes qui se sont réelement servies d'un
compilateur où ça faisait une différence réele ; je suis donc
assez sensibilisé sur le problème.
Non. (Mais je l'ai peut-être cru à une époque. Et si
threaded<>::needle utilisait bien les conventions d'appel C, ça
pourrait marcher.) Un « extern "C" » est ignoré sur une fonction
membre, même statique. (Et ne me démande pas pourquoi c'est
permis, si ça n'a aucun effet.)
Manuel Zaccaria wrote:
J'ai cru comprendre dans un autre post qu'on pouvait passer
par un typedef 'magique' si on avait vraiment besoin d'une
fonction membre à la fois «static» et «extern "c"».
Non. Rien qu'à partir du concepte, c'est impossible. Rien ne
dit que les deux langages passent leurs paramètres de la même
façon : le C++ pourrait les passer dans les registres, par
exemple, et le C sur la pile. (Si de telles différences me
semblent peu probables, j'ai déjà vu le cas sur Intel où la
responsibilité du nettoyage de la pile n'était pas la même.) Or,
si celui qui utilise le pointeur s'attend à une fonction C, il
va passer les paramètres comme le veut le C. Et ta fonction C++
va avoir du mal à s'y retrouver.
template<typename nullary_function>
struct return_type
{
typedef typename nullary_function::result_type type;
};
template<typename function_result_type>
struct return_type<function_result_type (*)()>
{
typedef function_result_type type;
};
Il y a d'autres solutions ; regarde, par exemple, comment fait
boost.threads. Sinon, la solution classique, c'est bien
d'utiliser une classe abstraite de base ; il y a bien une
conversion void* en AbstractThread* dans la fonction qu'appelle
le système, mais cette fonction est dans le sous-système des
threads. On assure donc une correction de type vis-à-vis des
utilisateurs.
static void *needle(void *eye)
Elle pourrait être une fonction libre templatée, sauf qu'une
fonction templatée ne peut pas être « extern "C" » non plus.
Mais...
Aussi, en passant, l'utilisation d'auto_ptr ici ne marche pas
avec tous les compilateurs ; le destructeur n'en serait pas
appelé par g++, par exemple, en cas de pthread_cancel.
Je ne vois rien dans ce code qui exige que la fonction soit
membre ; l'utilisation d'une classe abstraite de base fonctionne
aussi bien.
titre d'exercice, J'ai implémenté «threader»
de cette façon après avoir lu un truc (de toi?) sur c.l.c++.m.
Je doute que ce soit de moi. Par hazard, je suis une des rares
(apparamment) personnes qui se sont réelement servies d'un
compilateur où ça faisait une différence réele ; je suis donc
assez sensibilisé sur le problème.
Non. (Mais je l'ai peut-être cru à une époque. Et si
threaded<>::needle utilisait bien les conventions d'appel C, ça
pourrait marcher.) Un « extern "C" » est ignoré sur une fonction
membre, même statique. (Et ne me démande pas pourquoi c'est
permis, si ça n'a aucun effet.)
Manuel Zaccaria wrote:J'ai cru comprendre dans un autre post qu'on pouvait passer
par un typedef 'magique' si on avait vraiment besoin d'une
fonction membre à la fois «static» et «extern "c"».
Non. Rien qu'à partir du concepte, c'est impossible. Rien ne
dit que les deux langages passent leurs paramètres de la même
façon : le C++ pourrait les passer dans les registres, par
exemple, et le C sur la pile. (Si de telles différences me
semblent peu probables, j'ai déjà vu le cas sur Intel où la
responsibilité du nettoyage de la pile n'était pas la même.) Or,
si celui qui utilise le pointeur s'attend à une fonction C, il
va passer les paramètres comme le veut le C. Et ta fonction C++
va avoir du mal à s'y retrouver.
template<typename nullary_function>
struct return_type
{
typedef typename nullary_function::result_type type;
};
template<typename function_result_type>
struct return_type<function_result_type (*)()>
{
typedef function_result_type type;
};
Il y a d'autres solutions ; regarde, par exemple, comment fait
boost.threads. Sinon, la solution classique, c'est bien
d'utiliser une classe abstraite de base ; il y a bien une
conversion void* en AbstractThread* dans la fonction qu'appelle
le système, mais cette fonction est dans le sous-système des
threads. On assure donc une correction de type vis-à-vis des
utilisateurs.
static void *needle(void *eye)
Elle pourrait être une fonction libre templatée, sauf qu'une
fonction templatée ne peut pas être « extern "C" » non plus.
Mais...
Aussi, en passant, l'utilisation d'auto_ptr ici ne marche pas
avec tous les compilateurs ; le destructeur n'en serait pas
appelé par g++, par exemple, en cas de pthread_cancel.
Je ne vois rien dans ce code qui exige que la fonction soit
membre ; l'utilisation d'une classe abstraite de base fonctionne
aussi bien.
titre d'exercice, J'ai implémenté «threader»
de cette façon après avoir lu un truc (de toi?) sur c.l.c++.m.
Je doute que ce soit de moi. Par hazard, je suis une des rares
(apparamment) personnes qui se sont réelement servies d'un
compilateur où ça faisait une différence réele ; je suis donc
assez sensibilisé sur le problème.
Non. (Mais je l'ai peut-être cru à une époque. Et si
threaded<>::needle utilisait bien les conventions d'appel C, ça
pourrait marcher.) Un « extern "C" » est ignoré sur une fonction
membre, même statique. (Et ne me démande pas pourquoi c'est
permis, si ça n'a aucun effet.)
James Kanze a écrit:Manuel Zaccaria wrote:J'ai cru comprendre dans un autre post qu'on pouvait passer
par un typedef 'magique' si on avait vraiment besoin d'une
fonction membre à la fois «static» et «extern "c"».
Non. Rien qu'à partir du concepte, c'est impossible. Rien ne
dit que les deux langages passent leurs paramètres de la même
façon : le C++ pourrait les passer dans les registres, par
exemple, et le C sur la pile. (Si de telles différences me
semblent peu probables, j'ai déjà vu le cas sur Intel où la
responsibilité du nettoyage de la pile n'était pas la même.) Or,
si celui qui utilise le pointeur s'attend à une fonction C, il
va passer les paramètres comme le veut le C. Et ta fonction C++
va avoir du mal à s'y retrouver.
Certes! Tu as raison.
Je n'aurais pas dû retirer WINAPI dans le code que j'ai posté.
C'était plus du pseudo-code qu'autre chose. Il fallait lire ceci:
extern "C" typedef void* (WINAPI thread_function_t)(void*);
Ce qui spécifie la convention d'appel "pascal" sauf erreur.
C'est encore plus tordu donc et pas portable du tout (WIN32).
[...]template<typename nullary_function>
struct return_type
{
typedef typename nullary_function::result_type type;
};
template<typename function_result_type>
struct return_type<function_result_type (*)()>
{
typedef function_result_type type;
};
[...]Il y a d'autres solutions ; regarde, par exemple, comment
fait boost.threads. Sinon, la solution classique, c'est bien
d'utiliser une classe abstraite de base ; il y a bien une
conversion void* en AbstractThread* dans la fonction
qu'appelle le système, mais cette fonction est dans le
sous-système des threads. On assure donc une correction de
type vis-à-vis des utilisateurs.
La solution de Kevlin permet justement d'éviter de dériver
d'une ABC.
Si tu ne l'a pas encore fait et si tu a le temps, jette juste
un coup d'oeil sur le 1er pdf que j'ai pointé (c'est très vite
lu).
L'objet de type 'threadable' (le parametre de template) peut
être indifferement une fonction libre ou une classe avec un
'operator()'.
ex:
int any_nullary_fun()
{
return 42;
}
ou
class AnyJob // pas besoin de base
{
public:
typedef int result_type;
AnyJob(int n) : n(n) {}
int operator()()
{
return n*42;
}
private:
int n;
};
...
joiner<int> answer = thread(any_nullary_fun);
ou
joiner<int> answer = thread(AnyJob(24));
...
int result = answer();static void *needle(void *eye)
[...]Elle pourrait être une fonction libre templatée, sauf qu'une
fonction templatée ne peut pas être « extern "C" » non plus.
Mais...
Le C++ ne pourra pas ignorer les threads/callbacks indéfiniment...
Aussi, en passant, l'utilisation d'auto_ptr ici ne marche pas
avec tous les compilateurs ; le destructeur n'en serait pas
appelé par g++, par exemple, en cas de pthread_cancel.
Encore vrai mais windows ignore POSIX de ce coté, non ?
Pour la portabilité, boost est la voie à suivre de toute façon.
Je ne vois rien dans ce code qui exige que la fonction soit
membre ; l'utilisation d'une classe abstraite de base
fonctionne aussi bien.
Et sans base abstraite ? Qu'en penses-tu ?
titre d'exercice, J'ai implémenté «threader»
de cette façon après avoir lu un truc (de toi?) sur c.l.c++.m.
Je doute que ce soit de moi. Par hazard, je suis une des rares
(apparamment) personnes qui se sont réelement servies d'un
compilateur où ça faisait une différence réele ; je suis donc
assez sensibilisé sur le problème.
Sorry, j'ai confondu mais tu as participé au fil c'est sur :)Non. (Mais je l'ai peut-être cru à une époque. Et si
threaded<>::needle utilisait bien les conventions d'appel C, ça
pourrait marcher.) Un « extern "C" » est ignoré sur une fonction
membre, même statique. (Et ne me démande pas pourquoi c'est
permis, si ça n'a aucun effet.)
Oui mais c'est le typedef (hors de la classe) qui a un
linkage «extern "C"», pas le membre statique needle.
Bah... en résumé cette fonction:
a un linkage C
a une convention d'appel pascal
est membre statique d'une class template
James Kanze a écrit:
Manuel Zaccaria wrote:
J'ai cru comprendre dans un autre post qu'on pouvait passer
par un typedef 'magique' si on avait vraiment besoin d'une
fonction membre à la fois «static» et «extern "c"».
Non. Rien qu'à partir du concepte, c'est impossible. Rien ne
dit que les deux langages passent leurs paramètres de la même
façon : le C++ pourrait les passer dans les registres, par
exemple, et le C sur la pile. (Si de telles différences me
semblent peu probables, j'ai déjà vu le cas sur Intel où la
responsibilité du nettoyage de la pile n'était pas la même.) Or,
si celui qui utilise le pointeur s'attend à une fonction C, il
va passer les paramètres comme le veut le C. Et ta fonction C++
va avoir du mal à s'y retrouver.
Certes! Tu as raison.
Je n'aurais pas dû retirer WINAPI dans le code que j'ai posté.
C'était plus du pseudo-code qu'autre chose. Il fallait lire ceci:
extern "C" typedef void* (WINAPI thread_function_t)(void*);
Ce qui spécifie la convention d'appel "pascal" sauf erreur.
C'est encore plus tordu donc et pas portable du tout (WIN32).
[...]
template<typename nullary_function>
struct return_type
{
typedef typename nullary_function::result_type type;
};
template<typename function_result_type>
struct return_type<function_result_type (*)()>
{
typedef function_result_type type;
};
[...]
Il y a d'autres solutions ; regarde, par exemple, comment
fait boost.threads. Sinon, la solution classique, c'est bien
d'utiliser une classe abstraite de base ; il y a bien une
conversion void* en AbstractThread* dans la fonction
qu'appelle le système, mais cette fonction est dans le
sous-système des threads. On assure donc une correction de
type vis-à-vis des utilisateurs.
La solution de Kevlin permet justement d'éviter de dériver
d'une ABC.
Si tu ne l'a pas encore fait et si tu a le temps, jette juste
un coup d'oeil sur le 1er pdf que j'ai pointé (c'est très vite
lu).
L'objet de type 'threadable' (le parametre de template) peut
être indifferement une fonction libre ou une classe avec un
'operator()'.
ex:
int any_nullary_fun()
{
return 42;
}
ou
class AnyJob // pas besoin de base
{
public:
typedef int result_type;
AnyJob(int n) : n(n) {}
int operator()()
{
return n*42;
}
private:
int n;
};
...
joiner<int> answer = thread(any_nullary_fun);
ou
joiner<int> answer = thread(AnyJob(24));
...
int result = answer();
static void *needle(void *eye)
[...]
Elle pourrait être une fonction libre templatée, sauf qu'une
fonction templatée ne peut pas être « extern "C" » non plus.
Mais...
Le C++ ne pourra pas ignorer les threads/callbacks indéfiniment...
Aussi, en passant, l'utilisation d'auto_ptr ici ne marche pas
avec tous les compilateurs ; le destructeur n'en serait pas
appelé par g++, par exemple, en cas de pthread_cancel.
Encore vrai mais windows ignore POSIX de ce coté, non ?
Pour la portabilité, boost est la voie à suivre de toute façon.
Je ne vois rien dans ce code qui exige que la fonction soit
membre ; l'utilisation d'une classe abstraite de base
fonctionne aussi bien.
Et sans base abstraite ? Qu'en penses-tu ?
titre d'exercice, J'ai implémenté «threader»
de cette façon après avoir lu un truc (de toi?) sur c.l.c++.m.
Je doute que ce soit de moi. Par hazard, je suis une des rares
(apparamment) personnes qui se sont réelement servies d'un
compilateur où ça faisait une différence réele ; je suis donc
assez sensibilisé sur le problème.
Sorry, j'ai confondu mais tu as participé au fil c'est sur :)
Non. (Mais je l'ai peut-être cru à une époque. Et si
threaded<>::needle utilisait bien les conventions d'appel C, ça
pourrait marcher.) Un « extern "C" » est ignoré sur une fonction
membre, même statique. (Et ne me démande pas pourquoi c'est
permis, si ça n'a aucun effet.)
Oui mais c'est le typedef (hors de la classe) qui a un
linkage «extern "C"», pas le membre statique needle.
Bah... en résumé cette fonction:
a un linkage C
a une convention d'appel pascal
est membre statique d'une class template
James Kanze a écrit:Manuel Zaccaria wrote:J'ai cru comprendre dans un autre post qu'on pouvait passer
par un typedef 'magique' si on avait vraiment besoin d'une
fonction membre à la fois «static» et «extern "c"».
Non. Rien qu'à partir du concepte, c'est impossible. Rien ne
dit que les deux langages passent leurs paramètres de la même
façon : le C++ pourrait les passer dans les registres, par
exemple, et le C sur la pile. (Si de telles différences me
semblent peu probables, j'ai déjà vu le cas sur Intel où la
responsibilité du nettoyage de la pile n'était pas la même.) Or,
si celui qui utilise le pointeur s'attend à une fonction C, il
va passer les paramètres comme le veut le C. Et ta fonction C++
va avoir du mal à s'y retrouver.
Certes! Tu as raison.
Je n'aurais pas dû retirer WINAPI dans le code que j'ai posté.
C'était plus du pseudo-code qu'autre chose. Il fallait lire ceci:
extern "C" typedef void* (WINAPI thread_function_t)(void*);
Ce qui spécifie la convention d'appel "pascal" sauf erreur.
C'est encore plus tordu donc et pas portable du tout (WIN32).
[...]template<typename nullary_function>
struct return_type
{
typedef typename nullary_function::result_type type;
};
template<typename function_result_type>
struct return_type<function_result_type (*)()>
{
typedef function_result_type type;
};
[...]Il y a d'autres solutions ; regarde, par exemple, comment
fait boost.threads. Sinon, la solution classique, c'est bien
d'utiliser une classe abstraite de base ; il y a bien une
conversion void* en AbstractThread* dans la fonction
qu'appelle le système, mais cette fonction est dans le
sous-système des threads. On assure donc une correction de
type vis-à-vis des utilisateurs.
La solution de Kevlin permet justement d'éviter de dériver
d'une ABC.
Si tu ne l'a pas encore fait et si tu a le temps, jette juste
un coup d'oeil sur le 1er pdf que j'ai pointé (c'est très vite
lu).
L'objet de type 'threadable' (le parametre de template) peut
être indifferement une fonction libre ou une classe avec un
'operator()'.
ex:
int any_nullary_fun()
{
return 42;
}
ou
class AnyJob // pas besoin de base
{
public:
typedef int result_type;
AnyJob(int n) : n(n) {}
int operator()()
{
return n*42;
}
private:
int n;
};
...
joiner<int> answer = thread(any_nullary_fun);
ou
joiner<int> answer = thread(AnyJob(24));
...
int result = answer();static void *needle(void *eye)
[...]Elle pourrait être une fonction libre templatée, sauf qu'une
fonction templatée ne peut pas être « extern "C" » non plus.
Mais...
Le C++ ne pourra pas ignorer les threads/callbacks indéfiniment...
Aussi, en passant, l'utilisation d'auto_ptr ici ne marche pas
avec tous les compilateurs ; le destructeur n'en serait pas
appelé par g++, par exemple, en cas de pthread_cancel.
Encore vrai mais windows ignore POSIX de ce coté, non ?
Pour la portabilité, boost est la voie à suivre de toute façon.
Je ne vois rien dans ce code qui exige que la fonction soit
membre ; l'utilisation d'une classe abstraite de base
fonctionne aussi bien.
Et sans base abstraite ? Qu'en penses-tu ?
titre d'exercice, J'ai implémenté «threader»
de cette façon après avoir lu un truc (de toi?) sur c.l.c++.m.
Je doute que ce soit de moi. Par hazard, je suis une des rares
(apparamment) personnes qui se sont réelement servies d'un
compilateur où ça faisait une différence réele ; je suis donc
assez sensibilisé sur le problème.
Sorry, j'ai confondu mais tu as participé au fil c'est sur :)Non. (Mais je l'ai peut-être cru à une époque. Et si
threaded<>::needle utilisait bien les conventions d'appel C, ça
pourrait marcher.) Un « extern "C" » est ignoré sur une fonction
membre, même statique. (Et ne me démande pas pourquoi c'est
permis, si ça n'a aucun effet.)
Oui mais c'est le typedef (hors de la classe) qui a un
linkage «extern "C"», pas le membre statique needle.
Bah... en résumé cette fonction:
a un linkage C
a une convention d'appel pascal
est membre statique d'une class template
Manuel Zaccaria wrote:
En effet. Mais de toute façon, il s'agissait de lancer un
thread, qui n'est pas portable par définition. Si l'API du
système a des exigeances particulières, il faut y adhérer.
Mais du coup, évidemment, ça en rend plus difficile la
discussion. Étant donné que WINAPI est une extension du langage,
rien n'empèche que Microsoft permet l'utilisation de cette
extension sur des fonctions membres statiques -- je n'en sais
et Solaris. En revanche, si ça marche, je ne vois pas pourquoi
s'en servir, puisque le code n'est pas portable de toute façon.
interne qui en dérivera. Alors, je ne vois pas ce que ça change,
en dériver ou non.
Le C++ ne pourra pas ignorer les threads/callbacks indéfiniment...
Elle ne le ferait pas. On parle sérieusement du threading pour
la prochaine édition de la norme. Mais ça ne changera rien dans
les principes en question ici : si l'API veut l'adresse d'une
fonction C comme callback, il faut lui donner l'adresse d'une
fonction qui se comporte exactement comme une fonction C à son
interface. Une fonction C++ ne fera pas l'affaire, non plus
qu'une fonction Java, ni une fonction Ada. C'est pourquoi la
plupart des langages modernes ont le concepte d'un appel
« étranger », l'« extern "langage" » du C++, n'en est qu'une
exemple. (Essaie de faire la même chose en Java, par exemple.
Là, il te faut du JNI, ce qui est autrement compliqué que
« extern "C" ».)
Il doit bien y avoir quelque chose d'équivalent. Sinon, c'est
parfois un peu pénible : on n'ose pas vraiment bloquer le
thread sur une requête ; il faut plutôt mettre des hors-temps
assez courts, et faire du polling, ce qui utilise des ressources
machine pour rien.
Pour la portabilité, boost est la voie à suivre de toute façon.
Peut-être. C'est loin d'être certain que l'interface de Boost
soit celle adoptée. Elle a des faiblesses notoires.
Et sans base abstraite ? Qu'en penses-tu ?
J'ai mes doutes, au moins avec de la métaprogrammation des
templates. (C'est trivial avec de la vraie métaprogrammation, en
dehors du C++.) Mais je ne vois pas de raison non plus à perdre
du temps pour chercher une telle solution, non plus.
Je ne sais pas comment Boost s'en sort. Il se sert bien d'un
objet de type boost::function0, qu'il copie. Alors, a priori, je
suppose qu'il y a une fonction virtuelle cachée dedans, parce
que le comportement de l'objet varie bien selon le constructeur
utilisé ; il doit y avoir quelque chose du genre de l'idiome
lettre/enveloppe (mais il y a tellement de macros dans
l'implémentation de boost::function que je n'arrive pas à
suivre).
Oui mais c'est le typedef (hors de la classe) qui a un
linkage «extern "C"», pas le membre statique needle.
Mais ça ne change rien. L'« extern "C" » est ignoré sur une
fonction membre, statique ou non.
En ce qui concerne la convention d'appel pascal, c'est une
extension de Windows, dont je ne sais rien. En ce qui concerne
la norme, en revanche, il n'y a pas de fonction membre avec un
linkage "C". N'importe comment on s'y prend. Et à part
l'histoire de la convention d'appel Pascal, on a exactement le
même problème sous Unix.
Manuel Zaccaria wrote:
En effet. Mais de toute façon, il s'agissait de lancer un
thread, qui n'est pas portable par définition. Si l'API du
système a des exigeances particulières, il faut y adhérer.
Mais du coup, évidemment, ça en rend plus difficile la
discussion. Étant donné que WINAPI est une extension du langage,
rien n'empèche que Microsoft permet l'utilisation de cette
extension sur des fonctions membres statiques -- je n'en sais
et Solaris. En revanche, si ça marche, je ne vois pas pourquoi
s'en servir, puisque le code n'est pas portable de toute façon.
interne qui en dérivera. Alors, je ne vois pas ce que ça change,
en dériver ou non.
Le C++ ne pourra pas ignorer les threads/callbacks indéfiniment...
Elle ne le ferait pas. On parle sérieusement du threading pour
la prochaine édition de la norme. Mais ça ne changera rien dans
les principes en question ici : si l'API veut l'adresse d'une
fonction C comme callback, il faut lui donner l'adresse d'une
fonction qui se comporte exactement comme une fonction C à son
interface. Une fonction C++ ne fera pas l'affaire, non plus
qu'une fonction Java, ni une fonction Ada. C'est pourquoi la
plupart des langages modernes ont le concepte d'un appel
« étranger », l'« extern "langage" » du C++, n'en est qu'une
exemple. (Essaie de faire la même chose en Java, par exemple.
Là, il te faut du JNI, ce qui est autrement compliqué que
« extern "C" ».)
Il doit bien y avoir quelque chose d'équivalent. Sinon, c'est
parfois un peu pénible : on n'ose pas vraiment bloquer le
thread sur une requête ; il faut plutôt mettre des hors-temps
assez courts, et faire du polling, ce qui utilise des ressources
machine pour rien.
Pour la portabilité, boost est la voie à suivre de toute façon.
Peut-être. C'est loin d'être certain que l'interface de Boost
soit celle adoptée. Elle a des faiblesses notoires.
Et sans base abstraite ? Qu'en penses-tu ?
J'ai mes doutes, au moins avec de la métaprogrammation des
templates. (C'est trivial avec de la vraie métaprogrammation, en
dehors du C++.) Mais je ne vois pas de raison non plus à perdre
du temps pour chercher une telle solution, non plus.
Je ne sais pas comment Boost s'en sort. Il se sert bien d'un
objet de type boost::function0, qu'il copie. Alors, a priori, je
suppose qu'il y a une fonction virtuelle cachée dedans, parce
que le comportement de l'objet varie bien selon le constructeur
utilisé ; il doit y avoir quelque chose du genre de l'idiome
lettre/enveloppe (mais il y a tellement de macros dans
l'implémentation de boost::function que je n'arrive pas à
suivre).
Oui mais c'est le typedef (hors de la classe) qui a un
linkage «extern "C"», pas le membre statique needle.
Mais ça ne change rien. L'« extern "C" » est ignoré sur une
fonction membre, statique ou non.
En ce qui concerne la convention d'appel pascal, c'est une
extension de Windows, dont je ne sais rien. En ce qui concerne
la norme, en revanche, il n'y a pas de fonction membre avec un
linkage "C". N'importe comment on s'y prend. Et à part
l'histoire de la convention d'appel Pascal, on a exactement le
même problème sous Unix.
Manuel Zaccaria wrote:
En effet. Mais de toute façon, il s'agissait de lancer un
thread, qui n'est pas portable par définition. Si l'API du
système a des exigeances particulières, il faut y adhérer.
Mais du coup, évidemment, ça en rend plus difficile la
discussion. Étant donné que WINAPI est une extension du langage,
rien n'empèche que Microsoft permet l'utilisation de cette
extension sur des fonctions membres statiques -- je n'en sais
et Solaris. En revanche, si ça marche, je ne vois pas pourquoi
s'en servir, puisque le code n'est pas portable de toute façon.
interne qui en dérivera. Alors, je ne vois pas ce que ça change,
en dériver ou non.
Le C++ ne pourra pas ignorer les threads/callbacks indéfiniment...
Elle ne le ferait pas. On parle sérieusement du threading pour
la prochaine édition de la norme. Mais ça ne changera rien dans
les principes en question ici : si l'API veut l'adresse d'une
fonction C comme callback, il faut lui donner l'adresse d'une
fonction qui se comporte exactement comme une fonction C à son
interface. Une fonction C++ ne fera pas l'affaire, non plus
qu'une fonction Java, ni une fonction Ada. C'est pourquoi la
plupart des langages modernes ont le concepte d'un appel
« étranger », l'« extern "langage" » du C++, n'en est qu'une
exemple. (Essaie de faire la même chose en Java, par exemple.
Là, il te faut du JNI, ce qui est autrement compliqué que
« extern "C" ».)
Il doit bien y avoir quelque chose d'équivalent. Sinon, c'est
parfois un peu pénible : on n'ose pas vraiment bloquer le
thread sur une requête ; il faut plutôt mettre des hors-temps
assez courts, et faire du polling, ce qui utilise des ressources
machine pour rien.
Pour la portabilité, boost est la voie à suivre de toute façon.
Peut-être. C'est loin d'être certain que l'interface de Boost
soit celle adoptée. Elle a des faiblesses notoires.
Et sans base abstraite ? Qu'en penses-tu ?
J'ai mes doutes, au moins avec de la métaprogrammation des
templates. (C'est trivial avec de la vraie métaprogrammation, en
dehors du C++.) Mais je ne vois pas de raison non plus à perdre
du temps pour chercher une telle solution, non plus.
Je ne sais pas comment Boost s'en sort. Il se sert bien d'un
objet de type boost::function0, qu'il copie. Alors, a priori, je
suppose qu'il y a une fonction virtuelle cachée dedans, parce
que le comportement de l'objet varie bien selon le constructeur
utilisé ; il doit y avoir quelque chose du genre de l'idiome
lettre/enveloppe (mais il y a tellement de macros dans
l'implémentation de boost::function que je n'arrive pas à
suivre).
Oui mais c'est le typedef (hors de la classe) qui a un
linkage «extern "C"», pas le membre statique needle.
Mais ça ne change rien. L'« extern "C" » est ignoré sur une
fonction membre, statique ou non.
En ce qui concerne la convention d'appel pascal, c'est une
extension de Windows, dont je ne sais rien. En ce qui concerne
la norme, en revanche, il n'y a pas de fonction membre avec un
linkage "C". N'importe comment on s'y prend. Et à part
l'histoire de la convention d'appel Pascal, on a exactement le
même problème sous Unix.
"kanze" a écrit:Manuel Zaccaria wrote:
En effet. Mais de toute façon, il s'agissait de lancer un
thread, qui n'est pas portable par définition. Si l'API du
système a des exigeances particulières, il faut y adhérer.
Oui! Mais ça concerne également tous les callbacks disponible
sur une plateforme donnée. Pas seulement les threads. AMHA.
Mais du coup, évidemment, ça en rend plus difficile la
discussion. Étant donné que WINAPI est une extension du langage,
rien n'empèche que Microsoft permet l'utilisation de cette
extension sur des fonctions membres statiques -- je n'en sais
En ce qui concerne la convention d'appel (pas le linkage).
J'ai vérifié. C'est même permis sur les fonctions membres non
statiques selon la doc de Microsoft. Je ne vais pas exploiter
cette possibilité pour autant, même sous la torture.
et Solaris. En revanche, si ça marche, je ne vois pas
pourquoi s'en servir, puisque le code n'est pas portable de
toute façon.
Tu voulais dire "je ne vois pas pourquoi ne pas s'en servir" ?
Si Microsoft documente qu'on peut se servir de fonctions
membres statiques pour les callbacks, ce serais dommage de ne
pas en profiter si le reste du programme n'est pas portable
(ex: utilise DirectX) ?
interne qui en dérivera. Alors, je ne vois pas ce que ça
change, en dériver ou non.
Ca réduit juste un peu plus le couplage, non ? Ou pas du tout ?
Le C++ ne pourra pas ignorer les threads/callbacks
indéfiniment...
Elle ne le ferait pas. On parle sérieusement du threading
pour la prochaine édition de la norme. Mais ça ne changera
rien dans les principes en question ici : si l'API veut
l'adresse d'une fonction C comme callback, il faut lui
donner l'adresse d'une fonction qui se comporte exactement
comme une fonction C à son interface. Une fonction C++ ne
fera pas l'affaire, non plus
Mais comme ce callback est spécifique de la plateforme cible,
le programme n'est plus portable par définition... Alors,
qu'est-ce qui est acceptable finalement ?
Il doit bien y avoir quelque chose d'équivalent. Sinon,
c'est parfois un peu pénible : on n'ose pas vraiment bloquer
le thread sur une requête ; il faut plutôt mettre des
hors-temps assez courts, et faire du polling, ce qui utilise
des ressources machine pour rien.
TerminateThread(handle, exit_code);
...doit faire l'affaire, c'est pas du propre.
Pour la portabilité, boost est la voie à suivre de toute
façon.
Peut-être. C'est loin d'être certain que l'interface de Boost
soit celle adoptée. Elle a des faiblesses notoires.
Il y a mieux ? je suis preneur.
En ce qui concerne la convention d'appel pascal, c'est une
extension de Windows, dont je ne sais rien. En ce qui concerne
En fait je crois que c'est même pas du pascal car les paramètre
sont empilés de droite à gauche comme en C mais que c'est la
fonction appelée qui est responsable du dépilement au retour.
En conclusion et si j'ai bien saisi, le mieux que l'on puisse
faire, c'est une fonction libre (extern "C") et une classe de
base abstraite.
"kanze" a écrit:
Manuel Zaccaria wrote:
En effet. Mais de toute façon, il s'agissait de lancer un
thread, qui n'est pas portable par définition. Si l'API du
système a des exigeances particulières, il faut y adhérer.
Oui! Mais ça concerne également tous les callbacks disponible
sur une plateforme donnée. Pas seulement les threads. AMHA.
Mais du coup, évidemment, ça en rend plus difficile la
discussion. Étant donné que WINAPI est une extension du langage,
rien n'empèche que Microsoft permet l'utilisation de cette
extension sur des fonctions membres statiques -- je n'en sais
En ce qui concerne la convention d'appel (pas le linkage).
J'ai vérifié. C'est même permis sur les fonctions membres non
statiques selon la doc de Microsoft. Je ne vais pas exploiter
cette possibilité pour autant, même sous la torture.
et Solaris. En revanche, si ça marche, je ne vois pas
pourquoi s'en servir, puisque le code n'est pas portable de
toute façon.
Tu voulais dire "je ne vois pas pourquoi ne pas s'en servir" ?
Si Microsoft documente qu'on peut se servir de fonctions
membres statiques pour les callbacks, ce serais dommage de ne
pas en profiter si le reste du programme n'est pas portable
(ex: utilise DirectX) ?
interne qui en dérivera. Alors, je ne vois pas ce que ça
change, en dériver ou non.
Ca réduit juste un peu plus le couplage, non ? Ou pas du tout ?
Le C++ ne pourra pas ignorer les threads/callbacks
indéfiniment...
Elle ne le ferait pas. On parle sérieusement du threading
pour la prochaine édition de la norme. Mais ça ne changera
rien dans les principes en question ici : si l'API veut
l'adresse d'une fonction C comme callback, il faut lui
donner l'adresse d'une fonction qui se comporte exactement
comme une fonction C à son interface. Une fonction C++ ne
fera pas l'affaire, non plus
Mais comme ce callback est spécifique de la plateforme cible,
le programme n'est plus portable par définition... Alors,
qu'est-ce qui est acceptable finalement ?
Il doit bien y avoir quelque chose d'équivalent. Sinon,
c'est parfois un peu pénible : on n'ose pas vraiment bloquer
le thread sur une requête ; il faut plutôt mettre des
hors-temps assez courts, et faire du polling, ce qui utilise
des ressources machine pour rien.
TerminateThread(handle, exit_code);
...doit faire l'affaire, c'est pas du propre.
Pour la portabilité, boost est la voie à suivre de toute
façon.
Peut-être. C'est loin d'être certain que l'interface de Boost
soit celle adoptée. Elle a des faiblesses notoires.
Il y a mieux ? je suis preneur.
En ce qui concerne la convention d'appel pascal, c'est une
extension de Windows, dont je ne sais rien. En ce qui concerne
En fait je crois que c'est même pas du pascal car les paramètre
sont empilés de droite à gauche comme en C mais que c'est la
fonction appelée qui est responsable du dépilement au retour.
En conclusion et si j'ai bien saisi, le mieux que l'on puisse
faire, c'est une fonction libre (extern "C") et une classe de
base abstraite.
"kanze" a écrit:Manuel Zaccaria wrote:
En effet. Mais de toute façon, il s'agissait de lancer un
thread, qui n'est pas portable par définition. Si l'API du
système a des exigeances particulières, il faut y adhérer.
Oui! Mais ça concerne également tous les callbacks disponible
sur une plateforme donnée. Pas seulement les threads. AMHA.
Mais du coup, évidemment, ça en rend plus difficile la
discussion. Étant donné que WINAPI est une extension du langage,
rien n'empèche que Microsoft permet l'utilisation de cette
extension sur des fonctions membres statiques -- je n'en sais
En ce qui concerne la convention d'appel (pas le linkage).
J'ai vérifié. C'est même permis sur les fonctions membres non
statiques selon la doc de Microsoft. Je ne vais pas exploiter
cette possibilité pour autant, même sous la torture.
et Solaris. En revanche, si ça marche, je ne vois pas
pourquoi s'en servir, puisque le code n'est pas portable de
toute façon.
Tu voulais dire "je ne vois pas pourquoi ne pas s'en servir" ?
Si Microsoft documente qu'on peut se servir de fonctions
membres statiques pour les callbacks, ce serais dommage de ne
pas en profiter si le reste du programme n'est pas portable
(ex: utilise DirectX) ?
interne qui en dérivera. Alors, je ne vois pas ce que ça
change, en dériver ou non.
Ca réduit juste un peu plus le couplage, non ? Ou pas du tout ?
Le C++ ne pourra pas ignorer les threads/callbacks
indéfiniment...
Elle ne le ferait pas. On parle sérieusement du threading
pour la prochaine édition de la norme. Mais ça ne changera
rien dans les principes en question ici : si l'API veut
l'adresse d'une fonction C comme callback, il faut lui
donner l'adresse d'une fonction qui se comporte exactement
comme une fonction C à son interface. Une fonction C++ ne
fera pas l'affaire, non plus
Mais comme ce callback est spécifique de la plateforme cible,
le programme n'est plus portable par définition... Alors,
qu'est-ce qui est acceptable finalement ?
Il doit bien y avoir quelque chose d'équivalent. Sinon,
c'est parfois un peu pénible : on n'ose pas vraiment bloquer
le thread sur une requête ; il faut plutôt mettre des
hors-temps assez courts, et faire du polling, ce qui utilise
des ressources machine pour rien.
TerminateThread(handle, exit_code);
...doit faire l'affaire, c'est pas du propre.
Pour la portabilité, boost est la voie à suivre de toute
façon.
Peut-être. C'est loin d'être certain que l'interface de Boost
soit celle adoptée. Elle a des faiblesses notoires.
Il y a mieux ? je suis preneur.
En ce qui concerne la convention d'appel pascal, c'est une
extension de Windows, dont je ne sais rien. En ce qui concerne
En fait je crois que c'est même pas du pascal car les paramètre
sont empilés de droite à gauche comme en C mais que c'est la
fonction appelée qui est responsable du dépilement au retour.
En conclusion et si j'ai bien saisi, le mieux que l'on puisse
faire, c'est une fonction libre (extern "C") et une classe de
base abstraite.
Manuel Zaccaria wrote:"kanze" a écrit:Manuel Zaccaria wrote:
En ce qui concerne la convention d'appel (pas le linkage).
J'ai vérifié. C'est même permis sur les fonctions membres non
statiques selon la doc de Microsoft. Je ne vais pas exploiter
cette possibilité pour autant, même sous la torture.
Permis, c'est une chose -- « extern "C" » est bien permis sur
un fonction membre, mais par définition n'y a pas effet.
A priori, il n'y a aucun rapport essentiel entre les conventions
d'appel (c-à-d des choses comme l'ordre des paramètres, sur la
pile ou dans des registres, etc.) et que la fonction soit membre
ou non.
Seulement, pour des raisons évidentes, on veut que les
conventions se retrouvent dans la décoration de la fonction, de
façon à ne pas pouvoir appeler une fonction avec de mauvaises
conventions. Et dans le cas d'« extern "C" », il faut
également que la décoration soit la même qu'en C, c-à-d
typiquement soit rien, soit juste un _ avant le nom, mais sans
le moindre indication en ce qui concerne si la fonction est
membre, etc. C'est sans doute pour cette raison que le C++ ne
permet pas de linkage C sur des fonctions membres, même
statiques.
Quand il s'agit d'une fonctionnalité généralement disponible,
même si l'interface est propre à Microsoft, on pourrait se poser
la question. J'imagine, par exemple, que même si le nom de la
fonction dans l'API n'est pas pthread_create, il y a pas mal de
ressemblances dans sa fonctionnalité. Je dois bien isoler
l'appel, de façon à appeler une fonction différente, avec
éventuellement des paramètres différents, dans un ordre
différent. Mais de là à utiliser une fonction membre statique
dans un cas, et une fonction libre dans l'autre...
Mais comme ce callback est spécifique de la plateforme cible,
le programme n'est plus portable par définition... Alors,
qu'est-ce qui est acceptable finalement ?
Ça dépend de l'interface. Pour pthread_create, il faut bien que
la fonction soit « extern "C" ». Si tu essaies d'écrire
quelque chose qui pourrait servir sur les deux plateformes, et
que tu veux limiter les dépendances autant que se peut, c'est
probablement préférable d'utiliser une fonction libre sous
Windows aussi. Pour d'autres interfaces, en revanche, qui n'ont
pas d'équivalent sous Unix, ou où l'équivalent sous Unix part
d'une autre philosophie, qui impose une autre façon d'organiser
le code, je ne vois aucune raison particulière d'éviter les
extensions Microsoft (de même que j'utilise regulièrement des
extensions Posix dans mon propre code).
TerminateThread(handle, exit_code);
...doit faire l'affaire, c'est pas du propre.
Attention. Le nom de pthread_cancel est un peu trompeur, parce
qu'il n'impose pas l'arrêt de l'autre thread. Tout ce qu'il
fait, c'est de signaler que l'arrêt est souhaité. Par rapport à
une simple variable booléene, il y a comme différences : quand
le thread ciblé teste la variable, si elle est vrai, il se passe
à peu près l'équivalent d'une exception, et qu'il y a un test
implicit dans un certain nombre d'appels système autrement
bloquants, qui donc se débloquent (mais un thread peut dire ou
qu'il ne veut pas être cancelé du tout, ou qu'on peut le faire à
n'importe quel instant). Aussi, ce déblocage et le
positionnement de la variable sont atomiques, de façon à ce
qu'il ne peut pas y avoir des conditions de race.
Peut-être. C'est loin d'être certain que l'interface de Boost
soit celle adoptée. Elle a des faiblesses notoires.
Il y a mieux ? je suis preneur.
Mieux, je ne sais pas. Mais celle de Boost a quelques problèmes
sérieux, comme le fait qu'une exception non-catchée puisse
rendre un thread détaché. Elle ne permet pas non plus la
propagation d'une exception à travers un join.
En fait je crois que c'est même pas du pascal car les paramètre
sont empilés de droite à gauche comme en C mais que c'est la
fonction appelée qui est responsable du dépilement au retour.
Ce qui doit être le cas normal pour toute fonction C++
non-varargs, non ? (C'était le cas avec le compilateur Zortech,
dans le temps. Et avec le compilateur Intel 80386 dont je me
suis servi pour les systèmes embarqués.)
Manuel Zaccaria wrote:
"kanze" a écrit:
Manuel Zaccaria wrote:
En ce qui concerne la convention d'appel (pas le linkage).
J'ai vérifié. C'est même permis sur les fonctions membres non
statiques selon la doc de Microsoft. Je ne vais pas exploiter
cette possibilité pour autant, même sous la torture.
Permis, c'est une chose -- « extern "C" » est bien permis sur
un fonction membre, mais par définition n'y a pas effet.
A priori, il n'y a aucun rapport essentiel entre les conventions
d'appel (c-à-d des choses comme l'ordre des paramètres, sur la
pile ou dans des registres, etc.) et que la fonction soit membre
ou non.
Seulement, pour des raisons évidentes, on veut que les
conventions se retrouvent dans la décoration de la fonction, de
façon à ne pas pouvoir appeler une fonction avec de mauvaises
conventions. Et dans le cas d'« extern "C" », il faut
également que la décoration soit la même qu'en C, c-à-d
typiquement soit rien, soit juste un _ avant le nom, mais sans
le moindre indication en ce qui concerne si la fonction est
membre, etc. C'est sans doute pour cette raison que le C++ ne
permet pas de linkage C sur des fonctions membres, même
statiques.
Quand il s'agit d'une fonctionnalité généralement disponible,
même si l'interface est propre à Microsoft, on pourrait se poser
la question. J'imagine, par exemple, que même si le nom de la
fonction dans l'API n'est pas pthread_create, il y a pas mal de
ressemblances dans sa fonctionnalité. Je dois bien isoler
l'appel, de façon à appeler une fonction différente, avec
éventuellement des paramètres différents, dans un ordre
différent. Mais de là à utiliser une fonction membre statique
dans un cas, et une fonction libre dans l'autre...
Mais comme ce callback est spécifique de la plateforme cible,
le programme n'est plus portable par définition... Alors,
qu'est-ce qui est acceptable finalement ?
Ça dépend de l'interface. Pour pthread_create, il faut bien que
la fonction soit « extern "C" ». Si tu essaies d'écrire
quelque chose qui pourrait servir sur les deux plateformes, et
que tu veux limiter les dépendances autant que se peut, c'est
probablement préférable d'utiliser une fonction libre sous
Windows aussi. Pour d'autres interfaces, en revanche, qui n'ont
pas d'équivalent sous Unix, ou où l'équivalent sous Unix part
d'une autre philosophie, qui impose une autre façon d'organiser
le code, je ne vois aucune raison particulière d'éviter les
extensions Microsoft (de même que j'utilise regulièrement des
extensions Posix dans mon propre code).
TerminateThread(handle, exit_code);
...doit faire l'affaire, c'est pas du propre.
Attention. Le nom de pthread_cancel est un peu trompeur, parce
qu'il n'impose pas l'arrêt de l'autre thread. Tout ce qu'il
fait, c'est de signaler que l'arrêt est souhaité. Par rapport à
une simple variable booléene, il y a comme différences : quand
le thread ciblé teste la variable, si elle est vrai, il se passe
à peu près l'équivalent d'une exception, et qu'il y a un test
implicit dans un certain nombre d'appels système autrement
bloquants, qui donc se débloquent (mais un thread peut dire ou
qu'il ne veut pas être cancelé du tout, ou qu'on peut le faire à
n'importe quel instant). Aussi, ce déblocage et le
positionnement de la variable sont atomiques, de façon à ce
qu'il ne peut pas y avoir des conditions de race.
Peut-être. C'est loin d'être certain que l'interface de Boost
soit celle adoptée. Elle a des faiblesses notoires.
Il y a mieux ? je suis preneur.
Mieux, je ne sais pas. Mais celle de Boost a quelques problèmes
sérieux, comme le fait qu'une exception non-catchée puisse
rendre un thread détaché. Elle ne permet pas non plus la
propagation d'une exception à travers un join.
En fait je crois que c'est même pas du pascal car les paramètre
sont empilés de droite à gauche comme en C mais que c'est la
fonction appelée qui est responsable du dépilement au retour.
Ce qui doit être le cas normal pour toute fonction C++
non-varargs, non ? (C'était le cas avec le compilateur Zortech,
dans le temps. Et avec le compilateur Intel 80386 dont je me
suis servi pour les systèmes embarqués.)
Manuel Zaccaria wrote:"kanze" a écrit:Manuel Zaccaria wrote:
En ce qui concerne la convention d'appel (pas le linkage).
J'ai vérifié. C'est même permis sur les fonctions membres non
statiques selon la doc de Microsoft. Je ne vais pas exploiter
cette possibilité pour autant, même sous la torture.
Permis, c'est une chose -- « extern "C" » est bien permis sur
un fonction membre, mais par définition n'y a pas effet.
A priori, il n'y a aucun rapport essentiel entre les conventions
d'appel (c-à-d des choses comme l'ordre des paramètres, sur la
pile ou dans des registres, etc.) et que la fonction soit membre
ou non.
Seulement, pour des raisons évidentes, on veut que les
conventions se retrouvent dans la décoration de la fonction, de
façon à ne pas pouvoir appeler une fonction avec de mauvaises
conventions. Et dans le cas d'« extern "C" », il faut
également que la décoration soit la même qu'en C, c-à-d
typiquement soit rien, soit juste un _ avant le nom, mais sans
le moindre indication en ce qui concerne si la fonction est
membre, etc. C'est sans doute pour cette raison que le C++ ne
permet pas de linkage C sur des fonctions membres, même
statiques.
Quand il s'agit d'une fonctionnalité généralement disponible,
même si l'interface est propre à Microsoft, on pourrait se poser
la question. J'imagine, par exemple, que même si le nom de la
fonction dans l'API n'est pas pthread_create, il y a pas mal de
ressemblances dans sa fonctionnalité. Je dois bien isoler
l'appel, de façon à appeler une fonction différente, avec
éventuellement des paramètres différents, dans un ordre
différent. Mais de là à utiliser une fonction membre statique
dans un cas, et une fonction libre dans l'autre...
Mais comme ce callback est spécifique de la plateforme cible,
le programme n'est plus portable par définition... Alors,
qu'est-ce qui est acceptable finalement ?
Ça dépend de l'interface. Pour pthread_create, il faut bien que
la fonction soit « extern "C" ». Si tu essaies d'écrire
quelque chose qui pourrait servir sur les deux plateformes, et
que tu veux limiter les dépendances autant que se peut, c'est
probablement préférable d'utiliser une fonction libre sous
Windows aussi. Pour d'autres interfaces, en revanche, qui n'ont
pas d'équivalent sous Unix, ou où l'équivalent sous Unix part
d'une autre philosophie, qui impose une autre façon d'organiser
le code, je ne vois aucune raison particulière d'éviter les
extensions Microsoft (de même que j'utilise regulièrement des
extensions Posix dans mon propre code).
TerminateThread(handle, exit_code);
...doit faire l'affaire, c'est pas du propre.
Attention. Le nom de pthread_cancel est un peu trompeur, parce
qu'il n'impose pas l'arrêt de l'autre thread. Tout ce qu'il
fait, c'est de signaler que l'arrêt est souhaité. Par rapport à
une simple variable booléene, il y a comme différences : quand
le thread ciblé teste la variable, si elle est vrai, il se passe
à peu près l'équivalent d'une exception, et qu'il y a un test
implicit dans un certain nombre d'appels système autrement
bloquants, qui donc se débloquent (mais un thread peut dire ou
qu'il ne veut pas être cancelé du tout, ou qu'on peut le faire à
n'importe quel instant). Aussi, ce déblocage et le
positionnement de la variable sont atomiques, de façon à ce
qu'il ne peut pas y avoir des conditions de race.
Peut-être. C'est loin d'être certain que l'interface de Boost
soit celle adoptée. Elle a des faiblesses notoires.
Il y a mieux ? je suis preneur.
Mieux, je ne sais pas. Mais celle de Boost a quelques problèmes
sérieux, comme le fait qu'une exception non-catchée puisse
rendre un thread détaché. Elle ne permet pas non plus la
propagation d'une exception à travers un join.
En fait je crois que c'est même pas du pascal car les paramètre
sont empilés de droite à gauche comme en C mais que c'est la
fonction appelée qui est responsable du dépilement au retour.
Ce qui doit être le cas normal pour toute fonction C++
non-varargs, non ? (C'était le cas avec le compilateur Zortech,
dans le temps. Et avec le compilateur Intel 80386 dont je me
suis servi pour les systèmes embarqués.)