سؤال

لقد وجدت مؤخرًا مكتبة C أريد استخدامها في مشروع C ++ الخاص بي. تم تكوين هذا الرمز مع المتغيرات العالمية ويكتب إخراجها إلى الذاكرة التي أشار إليها مؤشرات ثابتة. عندما أقوم بتنفيذ مشروعي ، أرغب في تشغيل مثيلتين من برنامج C: واحدة مع التكوين A وواحد مع التكوين B. لا يمكنني تشغيل برنامجي مرتين ، لذلك أعتقد أن هناك خياران:

  • جعل C ++ Wrapper: المشكلة هنا هي أن فئة Wrapper يجب أن تحتوي على جميع المتغيرات العالمية/الثابتة التي لدى مكتبة C. نظرًا لأن الوظائف الموجودة في مكتبة C تستخدم تلك المتغيرات ، سأضطر إلى إنشاء قوائم وسيطة كبيرة جدًا لتلك الوظائف.
  • نسخ ولصق مكتبة C: هنا سأضطر إلى تكييف اسم كل وظيفة وكل متغير داخل مكتبة C.

أي واحد هو الحل الأسرع؟ هل هناك إمكانيات أخرى لتشغيل حالتين من مصدر 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 ;
}

في المثال أعلاه ، يُطلق على الاثنين واحدًا تلو الآخر ، ولكن يمكن لعلاج الخيوط الخاصة بك بدلاً من ذلك.

إنقاذ الدولة العالمية؟

إذا كان الكود الخاص بك متخلفًا واحدًا ، فيمكنك InterLeave استدعاء المقدمة الأولى والاتصال بالثاني من خلال توفير/إعادة ضبط الحالة العالمية. دعونا نستخدم نفس البنية أعلاه:

/* 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) هو الحل السريع ، في حين أن الثاني هو إعادة تشكيل الكود لكتابته بشكل صحيح (هناك أسباب وجيهة للغاية متغيرات عالمية تعبئ ، ويبدو أنك تعثرت على أحدها) ، و والثالث هو "اختراق" يتيح لك interleave المكالمتين.

من بين جميع الحلول الثلاثة ، سيجعل الثاني فقط من السهل لف هذا الرمز داخل فئات C ++ الآمنة في مؤشر الترابط إذا كان لا يزال هناك حاجة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top