2009-05-16 5 views
0

J'écris maintenant des wrappers pour les fonctions C++, de sorte qu'ils peuvent être utilisés à partir du code C. L'idée est de compiler les fichiers cpp en utilisant g ++ et les fichiers c en utilisant gcc, puis de les lier ensemble (!), Mais en n'exposant que les fonctions nécessaires aux programmes C, en les rendant disponibles dans un en-tête fichier 'test.h', comme si (ou peut-être test.hpp?):Défis dans l'écriture des wrappers pour les fonctions C++ afin qu'ils puissent être utilisés à partir du code C

(Notez que je ne pas exposer la fonction 'vecteur Tokenize (const string & str, string const & delimiters)')

test.h:

/* Header can be read by both C+ and C compilers, just the way we want! */ 
#ifndef TEST_H 
#define TEST_H 

#ifdef __cplusplus 
extern "C" { 
#endif 

#if defined(__STDC__) || defined(__cplusplus) 
extern int TokenizeC(const char* text, const char* delim, char ***output); /* ANSI C prototypes */ 
extern void reclaim2D(char ***store, unsigned int itemCount); 
#endif 

#ifdef __cplusplus 
} 
#endif 

#endif /* TEST_H */ 

test.cpp:

#include <string> 
#include <iostream> 
#include <vector> 

#include <assert.h> 

#include "test.h" 

using namespace std; 

vector<string> Tokenize(const string& str,const string& delimiters) 
{ 
vector<string> tokens; 

string::size_type delimPos = 0, tokenPos = 0, pos = 0; 

if(str.length() < 1) return tokens; 

while(1) 
{ 
    delimPos = str.find_first_of(delimiters, pos); 
    tokenPos = str.find_first_not_of(delimiters, pos); 

    if(string::npos != delimPos) 
    { 
    if(string::npos != tokenPos) 
    { 
     if(tokenPos < delimPos) tokens.push_back(str.substr(pos,delimPos-pos)); 
     else tokens.push_back(""); 
    } 
    else tokens.push_back(""); 

    pos = delimPos + 1; 
    } 
    else 
    { 
    if(string::npos != tokenPos) tokens.push_back(str.substr(pos)); 
    else tokens.push_back(""); 
    break; 
    } 
} 

return tokens; 
} 

int TokenizeC(const char* text, const char* delim, char ***output) 
{ 
    if((*output) != NULL) return -1; /* I will allocate my own storage, and no one tells me how much. Free using reclaim2D */ 

    vector<string> s = Tokenize(text, delim); 

    // There will always be a trailing element, that will be blank as we keep a trailing delimiter (correcting this issue would not be worth the time, so this is a quick workaround) 
    assert(s.back().length() == 0); // This will be nop'ed in release build 
    s.pop_back(); 

    (*output) = (char **)malloc(s.size() * sizeof(char *)); 

    for(vector <string>::size_type x = 0; x < s.size(); x++) 
    { 
     (*output)[x] = strdup(s[x].c_str()); 

     if(NULL == (*output)[x]) 
     { 
      // Woops! Undo all 
      // TODO : HOW to test this scenario? 

      for(--x; x >= 0; --x) 
      { 
       free((*output)[x]); 
       (*output)[x] = NULL; 
      } 

      return -2; 
     } 
    } 

    return x; /* Return the number of tokens if sucessful */ 
} 

void reclaim2D(char ***store, unsigned int itemCount) 
{ 
    for (int x = 0; itemCount < itemCount; ++x) 
    { 
     free((*store)[x]); 
     (*store)[x] = NULL; 
    } 

    free((*store)); 
    (*store) = NULL; 
} 

poc.c:

#include <stdio.h> 
#include "test.h" 

int main() 
{ 
    const char *text = "-2--4--6-7-8-9-10-11-", *delim = "-"; 

    char **output = NULL; 

    int c = TokenizeC(text, delim, &output); 

    printf("[*]%d\n", c); 

    for (int x = 0; x < c; ++x) 
    { 
     printf("[*]%s\n", output[x]); 
    } 

    reclaim2D(&output, c); 

    return 0; 
} 

Avez-vous remarqué quelque chose de mal?

Pour commencer, quand je courais ce programme, je suis "symbole de code Insatisfait '__gxx_personality_v0'"

Heureusement, il y a quelque chose ici: What is __gxx_personality_v0 for?

Une fois que je courais g ++ avec des options "-fno-exceptions -fno-rtti ", la sortie échoue maintenant avec" symbole de données Insatisfait '_ZNSs4_Rep20_S_empty_rep_storageE' "

Bien sûr, les deux environnements (celui à compiler - HP-UX B.11.23 ia64 et celui pour exécuter le binaire sur - HP-UX B.11.31 ia64) ont des versions de bibliothèque différentes (mais la même architecture), et cela ne devrait pas être un raison des erreurs.

Je voudrais également tester le cas marqué par "// TODO: comment tester ce scénario?", Mais cela peut attendre pour le moment.

Des pointeurs?

Répondre

3

La manière la plus simple d'éviter les symboles indéfinis lors de la liaison est de lier avec g ++ (pas gcc). Vous pouvez toujours compiler votre fichier .c avec gcc, cependant.

Veuillez également utiliser le système à la fois. L'erreur de lien peut disparaître si vous exécutez toutes vos commandes gcc et g ++ sur le même système (peu importe l'ancien ou le nouveau).

+0

Voici ce que j'ai fait: g ++ -c -fPIC -Wall -Wuninitialized -fno-exceptions -fno-rtti -O - D__hpuxita -mlp64 -I $ (INCLUDEPATH) $ <; gcc -c -fPIC -Wall -Wstrict-prototypes -Wuninitialized -O -D__hpuxita -mlp64 -I $ (INCLUDEPATH) $ <; gcc -AA -AA -mlp64 -shared -L $ (LIBPATH) $ (OBJS) -o $ (APP) – PoorLuzer

+0

Ah oui - toute compilation se fait sur une seule machine. – PoorLuzer

+0

+1, le problème est la dernière ligne là, vous devriez lier avec g ++ pas gcc qui inclura tous les trucs C++ nécessaires par défaut. –

2

Pour appeler une fonction C++ à partir de C, vous ne pouvez pas avoir de noms tronqués. Supprimez le test conditionnel pour __cplusplus où vous effectuez le extern "C". Même si vos fonctions seront compilées par un compilateur C++, l'utilisation de extern "C" l'empêchera de gérer les noms.

Voici un exemple:

Le fichier C.

/* a.c */ 
#include "test.h" 

void call_cpp(void) 
{ 
    cpp_func(); 
} 

int main(void) 
{ 
    call_cpp(); 
    return 0; 
} 

Le fichier d'en-tête.

/* test.h */ 
#ifndef TEST_H 
#define TEST_H 
extern "C" void cpp_func(void); 
#endif 

Le fichier CPP.

// test.cpp 
#include <iostream> 
#include "test.h" 

extern "C" void cpp_func(void) 
{ 
    std::cout << "cpp_func" << std::endl; 
} 

La ligne de commande du compilateur.

g++ a.c test.cpp 
+0

et je devrais utiliser gcc au lieu de g ++ pour lier - contrairement à ce qu'Evan a dit plus haut? – PoorLuzer

+0

retirer le test conditionnel pour obtenir aucun mangling nom semble jouer avec la déclaration de prototype dans le fichier d'en-tête parce que aboie gcc à moi « avertissement: déclaration implicite de la fonction« TokenizeC » – PoorLuzer

+0

Les travaux conditionnels que pour C++, où il devrait travailler. .. Je crois que ce n'est pas le problème. – PoorLuzer

1

Y a-t-il une raison pour laquelle vous n'avez pas envisagé de modifier Swig pour ce faire? Je me souviens qu'il y a une branche de développement de Swig pour faire juste cela ...

+0

J'étais, quand j'ai posé la question, sous la croyance que l'utilisation de Swig pour interfacer le code C++ avec C soit une solution de contournement. Je serais intéressé à regarder le chemin si - si vous aviez quelques liens. – PoorLuzer

+1

C'est difficile à trouver ... Je pense que la plupart des gens finissent par utiliser C++ pour le code de la colle, mais j'ai trouvé le lien suivant, et je pense que c'est le même lien que je me suis souvenu dans le passé. qu'il ya eu peu d'intérêt: http://swig.svn.sourceforge.net/viewvc/swig/branches/gsoc2008-maciekd/Doc/Manual/C.html – Arafangion

+0

Oui, j'utilise C/C++ dans le code de production, et je préférez réécrire le code pour avoir le moins de dépendances dans de tels cas. +1 pour le lien si. – PoorLuzer

Questions connexes