Проблемы в написании оболочек для функций C ++, чтобы их можно было использовать из кода C - PullRequest
0 голосов
/ 17 мая 2009

Сейчас я пишу оболочки для функций C ++, чтобы их можно было использовать из кода C.

Идея состоит в том, чтобы скомпилировать файлы cpp, используя g ++, и файлы c, используя gcc, затем связать их вместе (!), Но предоставив ТОЛЬКО те функции, которые необходимы, программам C, сделав их доступными в заголовочном файле. 'test.h' (или, может быть, test.hpp?) примерно так:

(Обратите внимание, как я не предоставляю функцию 'vector Tokenize (const string & str, const string & 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;
}

Вы замечаете что-то не так?

Для начала, когда я запустил эту программу, я получил "Символ неудовлетворенного кода '__gxx_personality_v0'"

К счастью, здесь что-то есть: Для чего __gxx_personality_v0?

После того, как я запустил g ++ с параметрами "-fno-exceptions -fno-rtti", вывод теперь завершается с ошибкой "Символ неудовлетворенных данных '_ZNSs4_Rep20_S_empty_rep_storageE'"

Конечно, две среды (одна для компиляции - HP-UX B.11.23 ia64 и другая для запуска двоичного файла - HP-UX B.11.31 ia64) имеют разные версии библиотеки (но с одинаковой архитектурой), и это не должно быть причиной ошибок.

Я также хотел бы проверить случай, помеченный "// TODO: КАК проверить этот сценарий?", Но это может подождать пока.

Есть указатели?

Ответы [ 3 ]

3 голосов
/ 17 мая 2009

Самый простой способ избежать неопределенных символов при связывании - это связать с g ++ (не с gcc). Вы все еще можете скомпилировать ваш файл .c с помощью gcc.

Также, пожалуйста, используйте систему за раз. Ошибка ссылки может исчезнуть, если вы выполните все команды gcc и g ++ в одной и той же системе (независимо от старой или новой).

2 голосов
/ 17 мая 2009

Чтобы вызвать функцию C ++ из C, вы не можете иметь искаженные имена. Удалите условный тест для __cplusplus, где вы делаете extern "C". Даже если ваши функции будут скомпилированы компилятором C ++, использование extern "C" позволит избежать искажения имен.

Вот пример:

Файл C.

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

void call_cpp(void)
{
  cpp_func();
}

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

Заголовочный файл.

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

Файл CPP.

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

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

Командная строка компилятора.

g++ a.c test.cpp
1 голос
/ 26 ноября 2010

Есть ли причина, по которой вы не рассматривали возможность модификации Swig для этого? Кажется, я помню, что в Swig есть ветка для разработки, которая делает именно это ...

...