Si vous enveloppez votre partie privée dans un espace de noms anonyme, alors ni std::abs
ni private_function
peut être vu dans la table des symboles :
namespace{
#include<cmath>
float private_function(float f)
{
return std::abs(f);
}
}
extern "C" float public_function(float f)
{
return private_function(f);
}
compilation (g++ 4.3.3):
g++ -shared -o libtest.so test.cpp -s
inspection :
# nm -DC libtest.so
w _Jv_RegisterClasses
0000200c A __bss_start
w __cxa_finalize
w __gmon_start__
0000200c A _edata
00002014 A _end
000004a8 T _fini
000002f4 T _init
00000445 T public_function
Juste pour noter qu'Ulrich Drepper a écrit un essai concernant (tous ?) les aspects de l'écriture de bibliothèques partagées pour Linux/Unix, qui couvre le contrôle des symboles exportés parmi de nombreux autres sujets.
C'était très pratique pour expliquer clairement comment exporter uniquement les fonctions d'une liste blanche à partir d'une bibliothèque partagée.
Votre utilisation de l'attribut de visibilité par défaut et -fvisibility=hidden doit être complétée par -fvisibility-inlines-hidden.
Vous devriez également oublier d'essayer de masquer les exportations stdlib, consultez ce bogue GCC pour savoir pourquoi.
De plus, si vous avez tous vos symboles publics dans un en-tête spécifique, vous pouvez les envelopper dans #pragma GCC visibility push(default)
et #pragma GCC visibility pop
au lieu d'utiliser des attributs. Cependant, si vous créez une bibliothèque multiplateforme, jetez un œil à Contrôle des symboles exportés des bibliothèques partagées pour une technique permettant d'unifier votre stratégie d'exportation DLL Windows et DSO Linux.
Donc la solution que nous avons pour l'instant est la suivante :
test.cpp
#include <cmath>
#include <vector>
#include <typeinfo>
struct private_struct
{
float f;
};
float private_function(float f)
{
return std::abs(f);
}
void other_private_function()
{
std::vector<private_struct> f(1);
}
extern "C" void __attribute__ ((visibility ("default"))) public_function2()
{
other_private_function();
}
extern "C" float __attribute__ ((visibility ("default"))) public_function1(float f)
{
return private_function(f);
}
exporte.version
LIBTEST
{
global:
public*;
local:
*;
};
compilé avec
g++ -shared test.cpp -o libtest.so -fvisibility=hidden -fvisibility-inlines-hidden -s -Wl,--version-script=exports.version
donne
00000000 A LIBTEST
w _Jv_RegisterClasses
U _Unwind_Resume
U std::__throw_bad_alloc()
U operator delete(void*)
U operator new(unsigned int)
w __cxa_finalize
w __gmon_start__
U __gxx_personality_v0
000005db T public_function1
00000676 T public_function2
Ce qui est assez proche de ce que nous recherchons. Il y a cependant quelques pièges :
- Nous devons nous assurer que nous n'utilisons pas le préfixe "exporté" (dans cet exemple simple "public", mais évidemment quelque chose de plus utile dans notre cas) dans le code interne.
- De nombreux noms de symboles se retrouvent toujours dans la table de chaînes, qui semble être due à RTTI, -fno-rtti les fait disparaître dans mes tests simples, mais c'est une solution plutôt nucléaire.
Je suis heureux d'accepter toutes les meilleures solutions que quelqu'un propose !