简体   繁体   中英

Sharing Source files between C# and C++

I have a project that is largely written in C#. I need to define a class for all of the error number "defines" for the API of this project. I'm trying to avoid writing/altering one of my many code generators to achieve this.

What I would like to do is be able to #include content (like the error defiles) directly into a C/C++ project. I defined them in C# as follows, and I did not use an enum for things you will see here:

using System;

namespace ProjectAPI {

[Serializable]
public sealed class ProjectError {

    public enum ProjectErrorClass {
        None            = -1,
        Undefined       = 0,
        Login,
        Store,
        Transaction,
        Heartbeat,
        Service,
        HTTPS,
        Uploader,
        Downloader,
        APICall,
        AutoUpdate,
        General
    }

    public enum ProjectErrorLevel {
        Unknown = -1,
        Success = 0,
        Informational,
        Warning,
        Critical,
    };

    /// <summary>
    /// PROJECT_ERROR_BASE - This is the base for all Project defined errors in the API.  Project Errors are defined as follows:
    ///   ProjectAPI error values are 32 bit values defined as follows:
    ///   3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
    ///   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
    ///  +---+---------------+-----------------------+------------------+
    ///  |Sev|Error Code Base| Error Class           |Unique Error Code |
    ///  +---+---------------+-----------------------+------------------+
    ///  where
    ///
    ///      Sev - is the severity code of the error (2 bits), and is defined as follows:
    ///          00 - Success (non-fatal)   0x00
    ///          01 - Informational         0x01
    ///          10 - Warning               0x02
    ///          11 - Error                 0x03
    ///
    ///      Error Code Base - is the starting point of all Project Errors, and is set at 0xA4 (8 Bits).
    ///
    ///      Error Class - is the error class, or API "Module" that caused the error (12 bits).
    ///
    ///      Code - the unique error code (10 bits). (0 - 1,023 (0x3FF)).
    /// </summary>

    private static readonly int ERR_SHIFT                       = 0x1E;
    private static readonly int BASE_SHIFT                      = 0x16;
    private static readonly int CLASS_SHIFT                     = 0x06;

    private static readonly int PROJECT_SEV_SUCCESS           = 0x00;
    private static readonly int PROJECT_SEV_INFO              = 0x01;
    private static readonly int PROJECT_SEV_WARN              = 0x02;
    private static readonly int PROJECT_SEV_ERROR             = 0x03;

    private static readonly int PROJECT_ERROR_BASE             = 0xA5;

    /// <summary>
    /// Project Error Class Constants:
    /// </summary>
    private static readonly int PROJECT_ERROR_CLASS_UNDEF     = 0x0010;   /// Undefined.
    private static readonly int PROJECT_ERROR_CLASS_LOGIN     = 0x0020;   /// LoginClass Error.
    private static readonly int PROJECT_ERROR_CLASS_STORE     = 0x0040;   /// Store Error.
    private static readonly int PROJECT_ERROR_CLASS_TRANS     = 0x0080;   /// Transaction Error.
    private static readonly int PROJECT_ERROR_CLASS_HEART     = 0x0100;   /// HeartBeat (Project Health Monitor) Error.
    private static readonly int PROJECT_ERROR_CLASS_SERV      = 0x0200;   /// Service Error.
    private static readonly int PROJECT_ERROR_CLASS_HTTP      = 0x0400;   /// HTTP/HTTPS Error.
    private static readonly int PROJECT_ERROR_CLASS_UPLOAD    = 0x0800;   /// Upload (Transactions) Error
    private static readonly int PROJECT_ERROR_CLASS_DOWNLOAD  = 0x1000;   /// Download (Transactions) Error
    private static readonly int PROJECT_ERROR_CLASS_APICALL   = 0x2000;   /// API Command/call error.
    private static readonly int PROJECT_ERROR_CLASS_UPDATE    = 0x4000;   /// Auto-Updater Errors.
    private static readonly int PROJECT_ERROR_CLASS_GEN       = 0x8000;   /// General Error.

    public static readonly int PROJECT_ERROR_UNKNOWN_ERROR    = ProjectErrCode(PROJECT_SEV_ERROR, PROJECT_ERROR_CLASS_GEN, 0x001);
    // Was...
    //    (((PROJECT_SEV_ERROR << ERR_SHIFT) | PROJECT_ERROR_BASE << BASE_SHIFT) | ((PROJECT_ERROR_CLASS_UNDEF << CLASS_SHIFT) | 0x0001));

    public static readonly int PROJECT_ERROR_UNKNOWN_HEARTBEAT_ERROR = ProjectErrCode(PROJECT_SEV_ERROR, PROJECT_ERROR_CLASS_HEART, 0x001);
...Snip...

...

I realize there are other things I could put into Enums, however my goal is to be able to compile this source with a C++ compiler as well. (There are functions missing in the sample above, namely the ProjectErrCode() which builds the final integer value of the error code OTF when called from the API.)

I was building the error constants as seen in the comments, and I can go back to that, but I'd rather write similar classes - one in C# one in C++ that can construct/deconstruct the error codes. My functions return the Severity of the error, the error class, and so on. The developer can ignore it, log it, pass it on to the UI, etc.

If I only had 5 or 10 error codes, this wouldn't be an issue. But I have well over 100 and really don't want to have to maintain a .cs and .h file with duplicate information. I can manage them in the .h file and have the CS code read them , but that is almost as much work as writing (modifying) a code generator.


How can I #define my way to a single source file, such that the C# compiler can compile it, just the same as the C/++ compiler could as well? I could just simply #include "ProjectErrors.cs" - Filename is not an issue there. I started off thinking I could do this by #define 'ing stuff like the using System; but pretty much got hung up there.

1) Use preprocessor. Some ifdefs and defines should do the trick, but it would be extremely messy.

2) Use C++/CLI. C++/CLI is a variant of C++ which is compiled into .Net assemblies. While .Net types and native types are separate entities, conversions between them are possible.

For example, you make define the header as completely native code with native enums and constants; you can then include this header both into a 100% native project and into a C++/CLI project, which would also provide conversions ( see this thread ) of the enums to corresponding .Net types.

If you don't want to have this in-between conversion layer, C++/CLI also gives you full power of C++ macros, so you could make a file with macros like ENUM_HEADER and CONSTANT and evaluate these into appropriate managed or native forms in fairly clean and straightforward way (which you can't do with C#, because it has much weaker preprocessor). The resulting assembly would then be essentially this header and appropriate macro definitions and nothing else.

3) Have the values defined in some external file (XML, INI, whatever...) and only implement loading logic in both C# and C++ (this might actually be the cleanest solution).

One option is to use T4 : thats a code generation language built into Visual Studio. It is mostly used in C#, but C++ apparantly works too .

One of the reasons why you will have a hard time with the preprocessor is that C# and C++ have the same syntax for comments: you wont be able to hide incompatible C# specific syntax for the C++ preprocessor using a comment. That said, you can try VB :).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM