Оболочка C ++ для библиотеки C - PullRequest
4 голосов
/ 22 апреля 2010

Недавно я нашел библиотеку C, которую хочу использовать в своем проекте C ++. Этот код настроен на глобальные переменные и записывает его вывод в память, указанную статическими указателями . Когда я выполняю свой проект, я хотел бы запустить 2 экземпляра программы на C: один с конфигурацией A и один с конфигурацией B. Я не могу позволить себе запустить мою программу дважды, поэтому я думаю, что есть 2 варианта:

  • Создание оболочки C ++ : Проблема здесь в том, что класс оболочки должен содержать все глобальные / статические переменные, которые есть в библиотеке C. Поскольку функции в библиотеке C используют эти переменные, мне придется создать очень большие списки аргументов для этих функций.
  • Копирование-вставка библиотека C: здесь мне придется адаптировать имя каждой функции и каждой переменной внутри библиотеки C.

Какое решение является самым быстрым? Существуют ли другие возможности для запуска 2 экземпляров одного и того же источника C?

Спасибо

Макс

Ответы [ 5 ]

3 голосов
/ 22 апреля 2010

C ++ -Wrapper
Вам будет проще, если вы вставите в класс "всю библиотеку" - только слегка измененную.*

В основном это декларативные изменения (такие как "уничтожение" статических и внешних объявлений).Вы должны будете выискивать статические переменные внутри методов, а также превращать их в члены

Отдельные пространства имен
Это уродливое решение, но может быть достаточно:

// impMyLib.h
namespace A 
{
  #include "c-lib.h"
}
namespace B
{
  #include "c-lib.h"
}

// impMyLib.cpp
namespace A 
{
  #include "c-lib.c"
}
namespace B
{
  #include "c-lib.c"
}

Если вам повезет, оптимизатору / компоновщику удастся свернуть идентичный код.Однако типы A:: и B:: не связаны.

2 голосов
/ 22 апреля 2010

IIUC, у вас есть, в основном, это:

extern int a;
extern int b;

void f();
void g(); 

, где a и b изменяют поведение f() и g().Это верно?

Если у вас есть это и вы хотите обернуть это в C ++, то вы можете сделать следующее:

class the_library {
public:
  the_library(int a, int b) : a_(a), b_(b) {}

  void f() {a=a_; b=b_; ::f();}
  void g() {a=a_; b=b_; ::g();}
private:
  int a_;
  int b_;

};

В зависимости от того, что у вас есть вместоиз a и b, это может быть не очень эффективно.

Конечно, как сказал Раки в комментариях, поскольку здесь используются глобальные переменные, это вовсе не потокобезопасно.

2 голосов
/ 22 апреля 2010

Если вы не можете позволить себе запустить его дважды, как насчет 3 раз? Вы могли бы написать крошечный интерфейсный процесс, который запускает два отдельных экземпляра вашей C-программы. С точки зрения использования он все равно будет выглядеть как один .exe, который вы запускаете только один раз, но за кулисами у вас будет родительский процесс с двумя дочерними элементами. Я понятия не имею, подойдет ли этот подход к вашим реальным потребностям, но он почти наверняка будет быстрее, чем любой из двух других вариантов.

0 голосов
/ 22 апреля 2010

Возможно, что-то ускользнуло от меня, но ...

... Глобальные переменные распределяются между потоками, а не процессами ...

Это означает, что в вашем случае вы можетеу меня работают два процесса одной и той же программы на C, и они не будут мешать одному другому, если они не работают с общей памятью процесса.

... Если вам нужны два экземпляра кода C, работающего втот же процесс ...

Тогда вы облажались.

TLS, возможно?

Либо вы можете запустить их в отдельных потоках и объявить глобальные переменные как Thread-Переменные локального хранилища.Например, в Visual C ++ следующий код:

int myGlobalVariable = 42 ;                 // Global variable
__declspec(thread) int myTLSVariable = 42 ; // Thread local variable

Каждый поток будет иметь свою собственную версию переменной.Таким образом, в конце потока вы можете скопировать содержимое в другое место.

Переписать код ...

Вам не нужно добавлять слой C ++ к этому.Вы можете сохранить свой код C и объявить все свои глобальные переменные в структуре:

/* C global variable */
int iMyGlobalVariable = 42 ;
const char * strMyGlobalString = NULL ;
short iMyShortData = 7 ;

/* C struct */
typedef struct MyStruct
{
   int iMyGlobalVariable ;
   const char * strMyGlobalString ;
   short iMyShortData ;
}
MyStruct ;

И затем вы модифицируете прототипы функций, чтобы принимать указатель на эту структуру в качестве первого параметра, а затем вместоизменяя глобальную переменную, вы изменяете член структуры:

/* old function */
int foo(char *p)
{
   /* fudge with the global variables */
   iMyShortData = 55 ;

   /* etc. */
   fooAgain("Hello World", 42) ;
}

, который становится:

/* new function */
int foo(MyStruct * s, char *p)
{
   /* fudge with the struct variables */
   s->iMyShortData = 55 ;

   /* etc. */
   fooAgain(s, "Hello World", 42) ;
}

Затем в основной вместо вызова первой функции вы вызываете ее, даваяэто указатель на правильную структуру.Вместо:

int main(int argc, char * argv[])
{
   bar(42, 55) ;
}

Вы пишете:

int main(int argc, char * argv[])
{
   MyStruct A = { /* initialize A's members if needed */ }  ;
   MyStruct B = { /* initialize B's members if needed */ }  ;

   bar(&A, 42, 55) ;
   bar(&B, 42, 55) ;

   return 0 ;
}

В приведенном выше примере они вызываются один за другим, но вместо этого вы можете запускать потоки.

Сохранение глобального состояния?

Если ваш код однопоточный, вы можете чередовать вызовы для первого экземпляра и вызовы для второго, сохраняя / сбрасывая глобальное состояние.Давайте используем ту же структуру, что и выше:

/* C global variable */
int iMyGlobalVariable = 42 ;
short iMyShortData = 7 ;

void saveState(MyStruct * s)
{
   s->iMyGlobalVariable = iMyGlobalVariable ;
   s->iMyShortData = iMyShortData ;
}

void resetState(const MyStruct * s)
{
   iMyGlobalVariable = s->iMyGlobalVariable ;
   iMyShortData = s->iMyShortData ;
}

И затем, при необходимости, вы вызываете функции сохранения и сброса:

int main(int argc, char * argv[])
{
   MyStruct A = { /* initialize A's members if needed */ }  ;
   MyStruct B = { /* initialize B's members if needed */ }  ;

   resetState(&A) ; /* now, we work on A */
   bar(42, 55) ;
   saveState(&A) ;  /* we save the progress on A */

   resetState(&B) ; /* now, we work on B */
   bar(42, 55) ;
   saveState(&B) ;  /* we save the progress on B */

   resetState(&A) ; /* now, we work on A */
   foo("Hello World", 3.14159) ;
   saveState(&A) ;  /* we save the progress on A */

   resetState(&B) ; /* now, we work on B */
   foo("Hello World", 3.14159) ;
   saveState(&B) ;  /* we save the progress on B */

   /* etc. */
   return 0 ;
}

Это может быть упаковано кодом C ++ для автоматической упаковки resetState./ saveState функции.Например:

struct MyWrapper
{
    void foo(const char * p, double d)
    {
       resetState(&m_s) ;
       foo(p, d) ;
       saveState(&m_s) ;
    }

    void bar(int i, short i2)
    {
       resetState(&m_s) ;
       bar(i, i2) ;
       saveState(&m_s) ;
    }

    MyStruct m_s ;
} ;

, который вы позволяете вам переписать основной как:

int main(int argc, char * argv[])
{
   MyWrapper A ;
   MyWrapper B ;

   A.bar(42, 55) ;
   B.bar(42, 55) ;

   A.foo("Hello World", 3.14159) ;
   B.foo("Hello World", 3.14159) ;

   // etc.

   return 0 ;
}

, который выглядит намного лучше, чем версия C.Тем не менее, MyWrapper не является потокобезопасным ...

Заключение

Первое решение (TLS) - это быстрое и грязное решение, а второе - рефакторинг кода для правильной его записи.(есть очень веские причины, по которым глобальные переменные осуждаются, и, по-видимому, вы наткнулись на одну из них), а третий - это «хак», позволяющий вам чередовать два вызова.

Из всех трех решенийтолько вторая позволит легко обернуть этот код в надежные, поточно-ориентированные классы C ++, если это необходимо.

0 голосов
/ 22 апреля 2010

Мне нравится идея здесь.Но я должен сделать указатель на каждую переменную, которую мне нужно изменить.Вот пример:

lib.h:

void f();
int g();

lib.c:

#include "lib.h"
extern int a;
extern int * output;

void f(){
    *output=(*output+6)*a;
}
int g(){
    return *output;
}

object.cc:

#include "lib.h"
#include <iostream>
using namespace std;

int a;
int * output;

class the_library {
public:
  the_library(int a, int * output) : a_(a), output_(output) {}

  void f() {a=a_; output=output_; ::f();}
  int g() {a=a_; output=output_; ::g();}
private:
  int a_;
  int * output_;

};

int main(){

    int out1=2;
    the_library icache(3,&out1);
    icache.f();
    cout<<"icache.f() -> icache is "<<icache.g()<<endl;
    icache.f();
    cout<<"icache.f() -> icache is "<<icache.g()<<endl;

    int out2;
    out2=8;
    the_library dcache(7,&out2);
    dcache.f();
    cout<<"dcache.f()\t-> icache is "<<icache.g()<<endl;
    cout<<"\t\t-> dcache is "<<dcache.g()<<endl;
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...