【问题标题】:How do I share a constant between C# and C++ code?如何在 C# 和 C++ 代码之间共享常量?
【发布时间】:2011-03-09 22:18:15
【问题描述】:

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

我最接近的方法是在 IDL 中定义常量,然后使用 MIDL 和 TLBIMP 将其放入 C# 可以使用的 DLL 中。然而,这似乎并没有暴露常量,或者至少我不知道如何让它这样做。也许它仅限于类型定义。

还有其他建议吗?

【问题讨论】:

    标签: c# c++ idl maintainability


    【解决方案1】:

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

    常量.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"

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

    【讨论】:

      【解决方案2】:

      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。

      【讨论】:

      • 或两者兼而有之。我会保留定义的常量,然后添加一个返回它的函数。这样你仍然可以在main() 中使用ACCELERATION_DUE_TO_GRAVITY
      【解决方案3】:

      对我的用例的其他解决方案不满意,因此编写了一个略显老套的解决方案,似乎更符合原始要求; 一个文件中的常量,可以内置到两个 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. 使用#include 包含在 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

      【讨论】:

      • 这是天才。很脏,但我喜欢它。
      【解决方案4】:

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

      由于您的常量可能在 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

      您可能可以在 PowerShell 中完成类似的工作,但我并不十分精通。


      现在这有点麻烦,所以我可以为您的特定问题建议一个可能更好的方法。根本不要使用常量。将地址放入配置文件,让您的 C# 和 C++ 代码在启动时读取它。

      这样,您可以轻松地共享价值并且它是可配置的,以防您将来想要更改它。

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          猜你喜欢
          • 2023-03-17
          • 1970-01-01
          • 2010-12-10
          • 2012-10-27
          • 2013-07-26
          • 1970-01-01
          • 2016-06-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多