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

12
我正在使用C#和WCF编写两个进程,对于一个进程我用的是C#和WCF,而对于另一个进程,我用的是C++和WWSAPI。我希望能够在一个地方定义用于两者通信的地址,并使得C#和C++均可使用它。这种情况是否可能?
最接近的做法是在IDL中定义常量,然后使用MIDL和TLBIMP将其转换为DLL,以便C#可以使用它。但是这似乎并没有暴露该常量,或者我无法弄清如何做到这一点。也许它仅限于类型定义。
还有其他建议吗?
5个回答

10

您可以创建一个单独的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"
只要你从"ConstantBridge"项目中引用literal,就不会产生运行时依赖关系。你可以使用ILSpy或ILdasm进行验证。C#中的constC++/CLI中的literal在编译期间被逐字复制到调用站点

7

C#和C++的常量模型有所不同。通常情况下,常量甚至不会出现在生成的C++二进制文件中——大部分情况下,它会自动被替换。

与其使用常量,不如创建一个返回常量的函数,并从C#中进行P/Invoke调用。

因此,

#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。

6
或者两种方式都可以。我会定义一个常量并添加一个返回该常量的函数。这样你仍然可以在main()中使用 ACCELERATION_DUE_TO_GRAVITY - user24359

6

我对其他解决方案不满意,因此编写了一个有点hacky的解决方案,似乎更适合原始请求; 在一个文件中定义一个常量,可以构建到两个 C# 和 C++ 项目中...

  1. 在.cs文件中,使用一个共同的位置存储版本信息。

就像这样:

// 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. 在C++项目中(在.cpp文件中)使用#include进行包含

就像这样:

//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

1
这太聪明了。虽然有点瑕疵,但我很喜欢它。 - snowcrash09

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执行这一步骤并不是我知道如何做的事情。您需要调查它是否可能。UNIX文本处理工具确实值得下载。我在几个盒子上安装了CygWin,但对于这种局部化的东西,您可以使用单独的GnuWin32软件包

您可能可以在PowerShell中完成类似的工作,但我对此并不是很熟悉。


现在这有点笨拙,所以我建议你可能有更好的方法来回答你的问题。根本不要使用常量。将地址放入配置文件中,并让您的C#和C ++代码在启动时读取它。

这样,您可以轻松地共享值,并且如果您将来想要更改它,则可以进行配置。


0
一个比为每个常量写方法更简便的替代方案可能是创建一个包含常量作为实例属性的类。你可以在C#中创建它,并通过COM接口将其公开给C ++。这比P / Invoke更容易且不容易出错,因为您不必担心获取所有类型和名称 - 编译器已经为您完成了。
注意:我没有尝试过这个,我只是推测它应该可以工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接