Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I share a constant between C# and C++ code?

I'm writing two processes using C# and WCF for one and C++ and WWSAPI for the second. I want to be able to define the address being used for communication between the two in a single place and have both C# and C++ use it. Is this possible?

The closest I've come is defining the constant in an IDL, then using MIDL and TLBIMP to get it into a DLL that can be consumed by C#. However this doesn't seem to expose the constant, or at least I can't figure out how to make it do so. Maybe it is limited to type definitions only.

Any other suggestions?

like image 920
dlanod Avatar asked Jun 30 '10 02:06

dlanod


3 Answers

You can create a separate C++/CLI project and define all your constants in a .h file. For example, create C++/CLI Class Library project called "ConstantBridge" and a C# project called "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"

Now, have the "CSharpProgram" project reference the "ConstantBridge" project. Your other native C++ projects can simply #include "Constants.h".

As long as you reference only literals from the ConstantBridge project, a runtime dependency will not be generated. You can verify using ILSpy or ILdasm. const in C# and literal in C++/CLI are copied "literally" to the call site during compilation.

like image 174
kizzx2 Avatar answered Oct 02 '22 11:10

kizzx2


C# and C++ have differing models for constants. Typically, the constant won't even be emitted in the resulting C++ binary -- it's automatically replaced where it is needed most of the time.

Rather than using the constant, make a function which returns the constant, which you can P/Invoke from C#.

Thus,

#include <iostream>
const double ACCELERATION_DUE_TO_GRAVITY = 9.8;
int main()
{
     std::cout << "Acceleration due to gravity is: " << 
         ACCELERATION_DUE_TO_GRAVITY;
}

becomes

#include <iostream>
extern "C" double AccelerationDueToGravity()
{
    return 9.8;
}
int main()
{
     std::cout << "Acceleration due to gravity is: " << 
         AccelerationDueToGravity();
}

which you should be able to P/Invoke from C#.

like image 31
Billy ONeal Avatar answered Oct 02 '22 12:10

Billy ONeal


Wasn't happy with the other solutions for my use case so coded up a slightly hacky solution that seems to fit the original request better; a constant in one file that can be built into both a C# and a C++ project...

  1. Version information in a .cs file, in a common location.

Like this:

// 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. Include in C# project using Add Existing Item... [Add As Link]
  2. Include in C++ project (in a .cpp file) with a #include

Like this:

//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. Reference in C# with: MyAppVersion.Number
  2. Reference in C++ with: MyAppVersion::Number
like image 27
Gaspode Avatar answered Oct 02 '22 11:10

Gaspode