如何在C#和C ++代码之间共享常量?

时间:2010-06-30 02:02:29

标签: c# c++ idl maintainability

我正在编写两个进程,一个使用C#和WCF,另一个使用C ++和WWSAPI。我希望能够在一个地方定义用于两者之间通信的地址,并让C#和C ++都使用它。这可能吗?

我最接近的是在IDL中定义常量,然后使用MIDL和TLBIMP将其转换为可由C#使用的DLL。然而,这似乎没有暴露常数,或者至少我无法弄清楚如何使它这样做。也许它仅限于类型定义。

还有其他建议吗?

5 个答案:

答案 0 :(得分:6)

您可以创建单独的C ++ / CLI项目,并在.h文件中定义所有常量。例如,创建名为“ConstantBridge”的C ++ / CLI类库项目和名为“CSharpProgram”的C#项目:

Constants.h

namespace Constants
{
    const int MAGIC_NUMBER = 42;
}

// String literals must be defined as macros
#define MAGIC_PHRASE "Hello World"

// Since stirngs must be macros it's arguably more consistent 
// to use `define` throughout. This is for demonstration purpose.

ConstantBridge.h

#include "Constants.h"

namespace ConstantBridge { public ref class ConstantBridge {
public:
    // The use of the `literal` keyword is important
    // `static const` will not work
    literal int kMagicNumber = Constants::MAGIC_NUMBER;
    literal String ^ kMagicPhrase = MAGIC_PHRASE;
};}

CSharpProgram.cs

Console.WriteLine(ConstantBridge.kMagicNumber); // "42"
Console.WriteLine(ConstantBridge.kMagicPhrase); // "Hello World"

现在,让“CSharpProgram”项目引用“ConstantBridge”项目。您的其他本机C ++项目可以只是#include "Constants.h"

只要您从literal项目引用 ConstantBridge,就不会生成运行时依赖项。您可以使用ILSpy或ILdasm进行验证。 C#中的const和C ++ / CLI中的literal在编译期间“逐字地”复制到调用站点

答案 1 :(得分:5)

C#和C ++有不同的常量模型。通常情况下,常量甚至不会在生成的C ++二进制文件中发出 - 它会在大多数时间需要的地方自动替换。

不是使用常量,而是创建一个返回常量的函数,你可以从C#调用它。

因此,

#include <iostream>
const double ACCELERATION_DUE_TO_GRAVITY = 9.8;
int main()
{
     std::cout << "Acceleration due to gravity is: " << 
         ACCELERATION_DUE_TO_GRAVITY;
}

变为

#include <iostream>
extern "C" double AccelerationDueToGravity()
{
    return 9.8;
}
int main()
{
     std::cout << "Acceleration due to gravity is: " << 
         AccelerationDueToGravity();
}

你应该能够从C#进行P / Invoke。

答案 2 :(得分:4)

对于我的用例的其他解决方案并不满意,所以编写了一个稍微有点hacky的解决方案,似乎更符合原始请求; 一个文件中的常量,可以构建到 一个C#和一个C ++项目中 ......

  1. .cs文件中的公共信息,位于公共位置。
  2. 像这样:

    // Version.cs
    public static class MyAppVersion
    {
        //build
        public static string Number = "1.0";
        public static string Phase = "Alpha";
    
        //configuration (these are the build constants I use, substitute your own)
    #if BUILD_SHIPPING
        public static string Configuration = "Shipping";
    #elif BUILD_DEVELOPMENT
        public static string Configuration = "Development";
    #elif BUILD_DEBUG
        public static string Configuration = "Debug";
    #else
        "build type not defined"
    #endif
    }
    
    1. 使用添加现有项目包含在C#项目中... [添加为链接]
    2. 包含在带有#include
    3. 的C ++项目(在.cpp文件中)

      像这样:

      //include version information into a .cpp
      #define class namespace
      #define public
      #define static
      #define string const char*
      #include "..\..\Version.cs" //or to where-ever your file is
      ;
      #undef class
      #undef public
      #undef static
      #undef string
      
      1. 在C#中引用:MyAppVersion.Number
      2. C ++参考:MyAppVersion::Number

答案 3 :(得分:1)

当我过去不得不做这些事情的时候,我只是在构建过程中添加了一个额外的预编译步骤,从而自动创建另一个文件。

由于你的常量可能在C#中的一个类中,你可以使用它作为源文件:

MyClass.cs:
    class MyClass {
        public const int NUM_MONTHS = 12;    //COMMON
        public const int YEAR_BASE = 1900;   //COMMON
    }

grep '//COMMON' MyClass.cs
    | sed -e 's/^ *public const [a-z][a-z]*/#define/'
          -e 's/ *= */ /'
          -e 's/;.*$//'
    >MyClass.h
grep '//COMMON' MyClass.cs | sed -e 's/ *public //' -e 's/;.*$/;/' >MyClass.hpp

这会给你:

MyClass.h:
    #define NUM_MONTHS 12
    #define YEAR_BASE 1900

MyClass.hpp:
    const int NUM_MONTHS = 12;
    const int YEAR_BASE = 1900;

现在,让Visual Studio执行该步骤并非我知道该怎么做。你必须调查它是否可能。 UNIXy文本处理工具真的值得下载。我在几个方框上安装了CygWin但是,对于本地化的内容,您可以使用个人GnuWin32 packages。{/ p>

你可能在PowerShell中做类似的工作,但我并不是那么精通。


现在这有点像kludge所以我可以建议一个可能更好的方式来解决你的问题。不要使用一个常量。将地址放入配置文件中,并在启动时读取C#和C ++代码。

这样,您可以轻松地分享价值它可配置,以防您以后想要更改它。

答案 4 :(得分:0)

每个常量的方法的更简单的替代方法可以是包含常量作为实例属性的类。您可以在C#中创建它,并通过COM接口将其公开给C ++。这比P / Invoke更容易且更不容易出错,因为您不必担心所有类型和名称都正确 - 这些都是由编译器为您完成的。

注意:我没有试过这个,我只是推测它应该工作。

相关问题