如何在C#和C ++代码之间共享常数?
-
01-10-2019 - |
题
我正在使用C#和WCF进行两个过程,第二个过程和wwsapi在第二个过程中编写。我希望能够定义用于单个位置两者之间的通信的地址,并同时使用c#和C ++使用它。这可能吗?
我最接近的是定义IDL中的常数,然后使用MIDL和TLBIMP将其放入C#可以消耗的DLL中。但是,这似乎并没有暴露出常数,或者至少我不知道如何做到这一点。也许仅限于键入定义。
还有其他建议吗?
解决方案
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#调用。
其他提示
您可以创建一个单独的C ++/CLI项目,并在A中定义所有常数 .h
文件。例如,创建名为“ ConstantBridge”的C ++/CLI类库项目和一个名为“ CSHARPPROGRAM”的C#项目:
常数
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.
恒定桥
#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。 const
在C#和 literal
在C ++/CLI中是 将“字面上的”复制到呼叫网站 在汇编期间。
对我的用例不满意的其他解决方案,因此编码了一个稍微适合原始请求的稍微削减的解决方案; 一个可以内置到一个文件中的常数 两个都 一个C#和C ++项目...
- .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
}
- 使用添加现有项目在C#项目中包括... [添加AS链接
- 在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
- 在C#中引用:
MyAppVersion.Number
- 在C ++中引用:
MyAppVersion::Number
过去,我不得不做这些事情时,我只是在构建过程中添加了一个额外的预编译步骤,该过程自动从另一个文件中创建一个文件。
由于您的常数可能会在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软件包.
您可能在Powershell上做类似的工作,但我并不是很精通。
现在,这有点笨拙,所以我可以为您的特定问题建议一种更好的方法。别 采用 一个常数。将地址放入配置文件中,并在启动时将其C#和C ++代码读取。
这样,您就可以毫不痛苦地分享价值 和 如果您想将来更改它,它是可配置的。
对于每个常数的方法,可以更轻松的替代方法是包含常数为实例属性的类。您可以在C#中创建它,并通过COM接口将其暴露于C ++。这比P/Invoke更容易且容易出错,因为您不必担心正确地获取所有类型和名称 - 编译器为您完成了所有类型和名称。
注意:我没有尝试过,我只是猜测 应该 工作。