Как мне разделить константу между C# и C++ кодом?

Я пишу два процесса, используя C# и WCF для одного и C++ и WWSAPI для второго. Я хочу иметь возможность определить адрес, используемый для связи между ними в одном месте, и использовать его как в C#, так и в C++. Это возможно?

Самое близкое, что я получил, - это определение константы в IDL, а затем использование MIDL и TLBIMP, чтобы получить ее в DLL, которая может использоваться C#. Однако, похоже, это не раскрывает константу, или, по крайней мере, я не могу понять, как заставить это сделать это. Может быть, он ограничен только определениями типов.

Любые другие предложения?

5 ответов

Решение

C# и C++ имеют разные модели для констант. Как правило, константа даже не генерируется в полученном двоичном файле C++ - она ​​автоматически заменяется там, где это требуется большую часть времени.

Вместо того, чтобы использовать константу, создайте функцию, которая возвращает константу, которую вы можете P/Invoke из 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();
}

который вы должны быть в состоянии P/Invoke из C#.

Вы можете создать отдельный проект C++/CLI и определить все свои константы в .h файл. Например, создайте проект библиотеки классов C++/CLI с именем "ConstantBridge" и проект C# с именем "CSharpProgram":

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 Project, зависимость во время выполнения не будет сгенерирована. Вы можете проверить, используя ILSpy или ILdasm. const в C# и literal в C++/CLI копируются "буквально" на сайт вызова во время компиляции.

Я не был доволен другими решениями для моего варианта использования, поэтому закодировал немного хакерское решение, которое, кажется, лучше соответствует первоначальному запросу; константа в одном файле, которая может быть встроена в проект 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

Когда мне приходилось делать такие вещи в прошлом, я просто добавил дополнительный этап предварительной компиляции в процесс сборки, который автоматически создает один файл из другого.

Поскольку ваши константы, вероятно, будут находиться внутри класса в 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++ прочитать его при запуске.

That way, you get to share the value painlessly and it's configurable in case you ever want to change it in future.

Более простой альтернативой методу для каждой константы может быть класс, содержащий константы в качестве свойств экземпляра. Вы можете создать его в C# и открыть его через COM-интерфейсы для C++. Это проще и менее подвержено ошибкам, чем P/Invoke, так как вам не нужно беспокоиться о том, чтобы все типы и имена были правильными - все это делает для вас компилятор.

Примечание: я не пробовал это, я только предполагаю, что это должно работать.

Другие вопросы по тегам