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

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

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

какие-то другие предложения?

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();
}

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


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

константы.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. информация о версии в a .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#, используя добавить существующий элемент... [Добавить Link]
  2. включить в проект C++ (в a .файл 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, но я не очень хорошо разбираюсь в этом.


теперь это немного kludge, поэтому я могу предложить, возможно, лучший способ для вас конкретного вопроса. Не использовать a постоянная вообще. Поместите адрес в файл конфигурации и попросите код C# и c++ прочитать его при запуске.

таким образом, вы можете поделиться значением безболезненно и он настраивается, если вы когда-нибудь захотите изменить его в будущем.


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

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