Вопрос

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

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

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

Спасибо,

Максимум

Это было полезно?

Решение

C ++ -WRAPPER
Вы упущете проще, вставляя «всю библиотеку» - только слегка уменьшен - в класс.

// C
static char resultBuffer[42];
void ToResult(int x) { ... }
char const * GetResult() { return resultBuffer; }

становится

// C++
class CMyImportantCLib
{
  private:
    char resultBuffer[42];
    void ToResult(int x) { ... } // likely, no code changes at all
    char const * GetResult() { return resultBuffer; }
} ;

Существуют в основном декларативные изменения (такие как «убийство» статические и обычные декларации). Вы должны были бы охотиться на статические переменные внутри методов, хотя и превратить их в члены, а также

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

// 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:: не связаны.

Другие советы

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

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, это может быть не ужасно эффективно.

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

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

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;
}

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

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

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

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

Тогда ты облажался.

TLS, возможно?

Либо вы можете запустить их в отдельных потоках и объявить глобальные переменные в виде переменных по нико-локальным хранилищам. Например, на 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 ++ для автоматической обертывания функций сброса / 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) - это решение Quick'n'dirty, в то время как второй рефакторинг кода, чтобы написать его правильно (есть очень хорошие причины, по которым глобальные переменные нахмурились, и, по-видимому, наткнулись на один из них), Третий - это «взломать», позволяющее вам проинформировать два звонка.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top