最近我发现了一个我想在C ++项目中使用的C库。 此代码使用全局变量配置,并将其输出写入静态指针指向的内存。 当我执行我的项目时,我希望运行2个C程序实例:一个配置A,一个配置B.我不能运行我的程序两次,所以我认为有两个选项:
哪一个是最快的解决方案? 是否还有其他可能来运行相同C源的2个实例?
谢谢,
最高
答案 0 :(得分:3)
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::
中的类型无关。
答案 1 :(得分:2)
如果你买不起两次,那么3次怎么样?你可以想象一个小的前端进程,它启动你的C程序的两个独立的实例。从使用角度来看,它看起来仍然只是一个.exe,你只运行一次,但在幕后,你有一个有两个孩子的父进程。我不知道这种方法是否适合您的实际需求,但几乎可以肯定比其他两种选择更快。
答案 2 :(得分:2)
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
,这可能不会非常有效。
当然,正如Raki在评论中所说,由于这是使用全局变量,因此它根本不是线程安全的。
答案 3 :(得分:0)
我喜欢这里的想法。但我应该指出我需要修改的每个变量。 这是一个例子:
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;
}
答案 4 :(得分:0)
也许有些事情让我无法接受......但
这意味着在你的情况下,你可以让同一个C程序的两个进程工作,并且它们不会干扰另一个进程,除非它们以某种方式与进程共享内存一起工作。
然后你被搞砸了。
您可以在单独的线程中启动它们,并将全局变量声明为Thread-Local-Storage变量。例如,在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) ;
}
然后,在main中,不是调用第一个函数,而是通过给它指向正确结构的指针来调用它。而不是:
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)是quick'n'dirty解决方案,而第二个解决方案是重构代码以正确编写它(有很好的理由全局变量不受欢迎,显然,你偶然发现其中一个),第三个是“hack”,使你可以交错这两个电话。
在所有这三个解决方案中,如果仍然需要,只有第二个解决方案可以很容易地将这些代码包装在健壮的,线程安全的C ++类中。