Blog Technique Menu


Blog Technique
Override/Overload en C++

Il y a un moment, j'écrivais un bout de code C++ et je me suis retrouvé face à un problème étrange:

 

#include <iostream>
 
class A
{
protected:
void some_func(const unsigned int& param1)
{
std::cout << "A::some_func(" << param1 << ")" << std::endl;
}
public:
virtual ~A() {}
 virtual void some_func(const unsigned int& param1, const char*)
{
some_func(param1);
}
};
 
class B : public A
{
public:
virtual ~B() {}
 virtual void some_func(const unsigned int& param1, const char*)
{
some_func(param1);
}
};
 
int main(int, char**)
{
A* t = new B();
t->some_func(42, "some char*");
 return 0;
}

 

et la réponse fut immédiate :

 

$ g++ -W -Wall -Werror test.cc
test.cc: In member function ‘virtual void B::some_func(const unsigned int&, const char*)’:
test.cc:24: error: no matching function for call to ‘B::some_func(const unsigned int&)’
test.cc:22: note: candidates are: virtual void B::some_func(const unsigned int&, const char*)

 

Je me suis demandé le pourquoi du comment et j'en étais même au point de douter de mon g++. Après une heure de bidouille, j'ai décidé de regarder dans le standard C++ et quelle fût ma surprise, g++ n'était pas en faute mais bon, comme on le dit souvent, le problème se trouve entre le clavier et la chaise. J'ai donc trouvé dans le paragraphe 10.2 du standard l'explication de mon problème :

En effet, le paragraphe sur la résolution des symboles précise très clairement que la redéfinition d'une méthode dans une classe dérivée va cacher toutes les méthodes avec le même nom plus haut dans la hiérarchie.

 

Le seul moyen de régler ce problème est donc de préciser quelle fonction on veut appeler c'est à dire A::some_func(…)

 

 
Compiler un projet C/C++ avec Autotools et libmysqlclient

Selon les architectures, la bibliothèque mysqlclient (fournie avec MySQL) ne se trouve absolument pas au même endroit :

- sous OS X /usr/local/mysql/lib

- sous Linux /usr/lib

Bien sûr il en est de même pour les headers :

- sous OS X /usr/local/mysql/include

- sous Linux /usr/include/mysql/

 

Naturellement, vous voudriez pouvoir écrire votre joli AC_CHECK_LIB de façon à linker directement avec la bonne bibliothèque sans avoir à donner l'argument -L à gcc/g++ (ce qui ne sera absolument plus indépendant de la plate-forme)

 

Q : Mais comment allons-nous faire simplement pour ne pas dépendre de la plate-forme sans écrire des choses horribles (que je ne citerai pas pour des raisons évidentes) ?

R : Tout simplement en utilisant un binaire fourni par MySQL qui s'appelle mysql_config (il se trouve dans le répertoire de MySQL).

 

mysql_config est un binaire qui ne sert "qu'à" fournir les différentes informations dont on a besoin. Si vous lancez mysql_config, vous verrez apparaître quelque chose de la sorte :

 

$ mysql_config
Usage: /usr/local/mysql/bin/mysql_config [OPTIONS]
Options:
--cflags         [-I/usr/local/mysql/include  -g -Os -arch x86_64 -fno-common   -D_P1003_1B_VISIBLE -DSIGNAL_WITH_VIO_CLOSE -DSIGNALS_DONT_BREAK_READ -DIGNORE_SIGHUP_SIGQUIT  -DDONT_DECLARE_CXA_PURE_VIRTUAL]
--include        [-I/usr/local/mysql/include]
--libs           [-L/usr/local/mysql/lib -lmysqlclient -lz -lm     -lmygcc]
--libs_r         [-L/usr/local/mysql/lib -lmysqlclient_r -lz -lm     -lmygcc]
--plugindir      [/usr/local/mysql/lib/plugin]
--socket         [/tmp/mysql.sock]
--port           [0]
--version        [5.1.40]
--libmysqld-libs [-L/usr/local/mysql/lib -lmysqld -ldl  -lz -lm       -lmygcc]

 

Bien évidemment, l'affichage donné ici dépend de votre configuration et de votre système d'exploitation (ici OS X 10.6)

 

Mais un problème subsiste : Si mysql_config n'est pas dans le PATH, comment va-t-on faire ?

Grâce à la macro d'autoconf : AC_PATH_PROG

 

La macro AC_PATH_PROG est définie de la façon suivante :

 

AC_PATH_PROG (variable, prog-to-check-for, [value-if-not-found], [path = ‘$PATH’])

 

On va donc écrire quelque chose du style :

 

AC_PATH_PROG([MYSQL_CONFIG],[mysql_config], , $PATH:/usr/bin:/usr/local/mysql/bin)

 

pour chercher dans le $PATH ainsi que dans /usr/bin et /usr/local/mysql/bin (ce qui recouvre 99% des cas). Vous pouvez bien évidemment chercher dans d'autres dossiers en rajoutant le dossier à AC_PATH_PROG.

 

Si la variable MYSQL_CONFIG ne vaut rien alors c'est soit que mysql_config n'est pas installé auquel cas mysql non plus soit c'est le mauvais PATH et il faudra la rajouter dans le cas d'une installation exotique.

 

Nous allons donc pouvoir écrire quelque chose du genre :

 

AC_DEFUN([MYSQL], [
AC_MSG_CHECKING(for mysql_config executable)
AC_PATH_PROG([MYSQL_CONFIG],[mysql_config], , $PATH/usr/bin:/usr/local/mysql/bin)
if test "x$MYSQL_CONFIG" = "x"
then
AC_MSG_RESULT(not found)
exit 3
else
 
IBASE=`$MYSQL_CONFIG --include`
LBASE=`$MYSQL_CONFIG --libs_r`
CPPFLAGS="$CPPFLAGS $IBASE"
LDFLAGS="$LDFLAGS $LBASE"
AM_CXXFLAGS="$AM_CXXFLAGS $IBASE $LBASE"
AC_CHECK_HEADERS(mysql.h, AC_CHECK_LIB(mysqlclient_r, mysql_init, ,AC_MSG_ERROR(MyProject requires mysql)))
fi
])

 

J'utilise ici la version thread safe de la bibliothèque pour des raisons techniques. Si vous n'en n'avez pas besoin, changez la ligne

LBASE=`$MYSQL_CONFIG --libs_r`

en

LBASE=`$MYSQL_CONFIG --libs`

Il nous suffit d'appeler la fonction MYSQL() que l'on vient de définir juste avant vos AC_CHECK_LIB.

 

Je vous conseille, pour une plus grande propreté, de ne pas définir la fonction dans le configure.ac/in mais dans un fichier mysql.m4. Il vous suffira ensuite de créer un fichier acinclude.m4 (dans le même dossier que le configure.ac/in) avec comme contenu :

m4_include([chemin/vers/mysql.m4])