在Visual Studio(C ++ / CLI)中编译为托管.NET的C ++代码,否则编译为本机C ++

时间:2014-01-16 19:47:32

标签: c++ .net visual-studio g++ c++-cli

我有一个可以在Linux上编译的C ++库,并希望在Visual Studio(.NET兼容代码)中编译相同的代码。

我在Visual Studio中成功编译了相同的本机代码(不涉及图形)。但是,我想将一些类暴露给.NET。我可以为一些非托管类写一个托管包装 - 如果我花时间我没有。但是,对于一些简单的类,似乎在开头使用指令

#ifdef _MSC_VER
    public ref class myclass
#else
    class myclass
#endif 

可以做到这一点。代码编译为在visual studio中管理,否则编译为本机。但是,某些涉及指针的类在编译时会返回错误。我知道“*”是非托管指针,“^”是托管指针。我可以定义

#ifdef _MSC_VER
    #define POINTER ^
#else
    #define POINTER *
#endif 

这是经常修改的研究代码。编写包装器非常耗时,并且每次修改本机类时都需要修改包装器。因此,我更愿意使用上面的条件语句(但我想尽可能少地使用)。是否有编写在本机C ++和C ++ / CLI之间最大兼容的代码的教程。

提前致谢

2 个答案:

答案 0 :(得分:0)

C ++ / CLI ref指针与*指针不同。没有1对1等价。 ref ^指针指的是CLR中定义的“引用类型”(与“值类型”相对)的对象,它们可以用gcnew实例化。 Gcnew非常重要,因为它告诉编组人员要创建的对象是垃圾收集,因此这些类型不能与本机代码一起使用,需要对它们进行编组。但是,为了通过引用实际传递某些内容,例如为了能够回写一个函数中传递的参数,你还需要使用%...所以Form ^ myForm,如果被传递以获得返回值需要写成Form ^%myForm。

最好的办法是编写一个包装器并使用混合组装范例。

我不确定是来自Linux C ++代码,但要从Windows中的单词go执行此操作,您将创建一个MFC C ++库(或可执行文件),然后手动打开公共语言运行库并手动添加AssemblyInfo。 cpp和app.manifest文件到项目。

从那里开始,在你的代码中,有一个#pragma用于定义代码的哪些部分。

对于运行本机或托管上下文之外的代码,您可能希望编写:

#pragma unmanaged

告诉编译器将其后的所有内容视为非托管代码。

要为同一文件中的一段代码重新打开托管代码:

#pragma managed

依此类推,等等。

看起来确实可能需要编写包装器。它不一定是一个完整的包装器......也许只是一个返回最终结果的入口点,无论你想要做什么......以及任何需要完成的独特编程,都应该在一个混合程序集二进制文件,.NET类型只针对最面向目标的任务公开。

答案 1 :(得分:0)

我结束了一个头文件,允许我在托管(在Visual Studio下)或非托管模式下编译。

我必须做的另一个改变是删除类定义中的所有对象实例。例如,我必须将所有“字符串”对象更改为指针“string *”(然后还更改声明赋值等...),以便将它们编译为非托管代码和托管代码。

#ifndef COMMONHEADER_H_
#define COMMONHEADER_H_

#ifdef _MSC_VER
#define CLASS public ref class
#define POINTER ^
#define NEW gcnew
#define ARRAY(A, B) array<A ^> ^ B
#define VECTOR(A, B) array<A> ^ B
#define NEWARRAY(A, SIZE) gcnew array<A ^>(SIZE)
#define NULLPTR nullptr
#else
#define CLASS class
#define POINTER *
#define NEW new
#define ARRAY(A, B) A * * B
#define VECTOR(A, B) vector<A> * B
#define NEWARRAY(A, SIZE) new A*[SIZE];
#define NULLPTR NULL
#endif

#endif /* COMMONHEADER_H_ */

然后我继续在我的班级定义中使用这些。我用POINTER替换了所有对象指针“*”,用“NEW”等替换了所有“new”实例......

CLASS myclass
{
public:
    MyObject POINTER obj1;
    ARRAY(MyObject, objArray);
...

在某些情况下,我还必须为我的一些函数编写一个包装器。例如,下面重载一个带有托管字符串的函数,并将其传递给将非托管字符串作为输入的函数。

#ifdef _MSC_VER
bool saveToTxtFile(array<unsigned char>^ file) { pin_ptr<unsigned char> tmp = &file[0]; return saveToTxtFile((char *)tmp); }
#endif

或者在下面将非托管数据阵列作为托管数据阵列返回。

#ifdef _MSC_VER
array<double> ^ getValues() {
     array<double> ^tmpArray = gcnew array<double>(numberOfSamples);
     for (int ind1 = 0; ind1 < numberOfSamples; ind1++) tmpArray[ind1] = data[ind1];
     return tmpArray;
}
#endif

我知道编写一个完整的包装器更优雅,并且从长远来看可能比使用这个技巧更强大。但是,我有大约2000行代码,它节省了我很多时间,不必为我的30个对象编写包装器。此外,这样,我不必维护包装器,以防我更改类,我不需要加倍类的数量。最后一个优点是,当我的代码编译为托管代码时,我的所有对象都由垃圾收集器处理。

所以,总而言之,它不是最优雅的解决方案,但它是可行的并且有效。