【问题标题】:Sharing an enum from C#, C++/CLI, and C++共享来自 C#、C++/CLI 和 C++ 的枚举
【发布时间】:2011-03-15 11:49:09
【问题描述】:

我有一个由三部分组成的库。首先是提供实际功能的本机 C++。其次是 C++ 库的 C++/CLI 包装器/适配器,以简化 C# 到 C++ 的转换。最后我有一个 C# 库,它通过 C++/CLI 适配器调用 C++ 库。

现在我有两组并行枚举定义,一组存储在 .cs 文件中,另一组存储在 .h 文件中。这带来了双重问题:

  1. 我有双重维护。我必须始终在两个文件位置同步枚举的更改。
  2. 两个枚举使用的命名空间应该相同,但 C++/CLI 包装器会查看两组枚举并在它们之间进行转换,这会导致命名冲突。

现在我不确定像 thisthat 这样的解决方案能否解决两个问题。想法?

【问题讨论】:

  • 不是重复但看到类似的问题:stackoverflow.com/questions/954321/…
  • @Billy:它不是重复的,因为这里的 C++/CLI 层提供了一些额外的选项。不过,您给出的帖子中的答案在这里也可能会有所帮助。
  • 将这两个解决方案放在一起就完成了?!
  • 附加要求:我的 C# 客户端是“AnyCPU”并且支持的 C++/CLI 必须同时支持 x86/x64 部署。这意味着 C# 客户端在运行时显式加载 C++/CLI;从 C# 到 C++/CLI 的构建时依赖不是一个选项。

标签: c# .net c++ enums c++-cli


【解决方案1】:

即使您在本机 C++ 中包含 C# 枚举(如您的 first link 中所建议的那样),两个枚举也不是“相同的”,C++ 枚举不过是命名整数的列表,而 C# 枚举是源自枚举。因此,当您尝试同时使用它们时,您会在 C++/CLI 中遇到冲突。

一种可能的解决方案是使用预处理器,以便您的 C++/CLI 程序集在不同的命名空间中看到两个枚举:

// shared_enum.h

#undef ENUMKEYWORD
#undef ENUMNAMESPACE

#ifdef MANAGED
#define ENUMKEYWORD public enum class
#define ENUMNAMESPACE EnumShareManaged
#else
#define ENUMKEYWORD enum
#define ENUMNAMESPACE EnumShare
#endif

namespace ENUMNAMESPACE
{
    ENUMKEYWORD MyEnum
    {
        a = 1,
        b = 2,
        c = 3,
    };
}

在您的 C++/CLI 代码中,进行这样的包含:

#undef MANAGED
#include "shared_enum.h"
#define MANAGED
#include "shared_enum.h"

这使您可以区分这两种枚举 EnumShare::MyEnumEnumShareManaged::MyEnum 在您的 C++/CLI 代码中。

编辑:刚刚发现 this SO post 显示了在非托管和托管枚举之间进行转换的正确方法,这肯定也可以在这里工作。例如,在 C++/CLI 中,从托管枚举到非托管枚举的转换可以这样完成:

void MyWrapperClass::MyWrapperFunction(EnumShareManaged::MyEnum mx)
{
    EnumShare::MyEnum nx = static_cast<EnumShare::MyEnum>(mx);
    // call a native function "func"
    func(nx);
}

【讨论】:

  • C# 程序集如何查看“shared_enum.h”文件?我猜想让 C++ #include 一个“shared_enum.cs”比让 C# 以某种方式吸收一个“shared_enum.h”更容易。是吗?
  • @Mystagogue:如果您在您的 CPP 文件(C++/CLI 项目的)之一中包含“托管”shared_enum.h,托管枚举将成为程序集的一部分,如果您引用此来自 C# 的程序集,因为它被声明为“public”,所以它在那里可见。另一方面,如果您在“shared_enum.cs”中声明托管枚举,它将在您的 C++/CLI 项目中不可见,因为 C++/CLI 不引用您的 C# 项目,反之亦然。
  • 啊!是的,但是该死!附加要求:我需要同时支持 x86 和 x64 部署。我的“AnyCPU”C# 客户端代码当前引用了一个包含接口和枚举定义的中间人 C# 程序集。 C++/CLI x86/x64,也引用了中间人,在运行时由 C# 客户端根据体系结构显式加载。简而言之,我不能使用依赖于 C#“构建时”对 C++/CLI 程序集的引用的技术。
  • @Mystagogue:是的,当我阅读您提供的第一个参考的解决方案(#include "shared_enum.cs ”)。但我认为这将是一些可以避免的开销,所以我制定了一个更适合您首先描述的情况的解决方案。尽管如此,即使您使用中间人 C# 程序集,基本问题仍然相同 - 两种类型的枚举必须位于不同的命名空间中。恕我直言,最干净的事情是使用简单的代码生成器(就像 Alex Farber 建议的那样),因为 C# 缺少预处理器。
【解决方案2】:

考虑编写代码生成器程序,它通过枚举读取本机 h 文件并生成另一个 h 文件,将 enum 转换为 C++/CLI 枚举类。此类代码生成器可用于 C++/CLI 项目中的自定义构建步骤,生成所需的 CLI 枚举。

我使用这种方法生成本地包装类,以在非托管 C++ 中获取 Enum::GetNames 和 Enum::GetName 函数。

【讨论】:

  • 在一个大型项目中处理大约 50 个枚举时也使用了这种方法。最重要的部分是将托管枚举枚举器设置为未管理的枚举器值。
  • 即使使用代码生成器(如果您有很多枚举,这可能很有用),您也必须以某种方式处理命名冲突问题。我的建议是“使用不同的命名空间并正确处理它们”。有更好的主意吗?
【解决方案3】:

只需将您的 #include "Enum.cs" 指令放在外部命名空间中即可解决命名冲突。

编辑:Brent 建议的一种变体是使用 #define 来替换 .cs 文件中声明的命名空间之一(甚至是枚举名称本身)。这也避免了命名冲突,而不会使命名空间层次结构更深。

【讨论】:

  • 您的方法不允许在 C# 和本机 C++ 项目中直接使用相同的源文件。我的不需要宏。
  • 您的方法不允许从 C++/CLI 项目访问托管枚举,只能访问非托管枚举。所以不符合OP的要求。
  • 实际上,这种方法确实允许在以下情况下访问托管枚举:(1) 我正在使用仅包含枚举的 C# 中间人项目,(2) C++/CLI 项目引用了“中间人”C# 项目,并且 (3) 我使用了#include“enum.cs”及其周围的命名空间。这种方法也可以处理我的 x86/x64 部署要求。但是这种方法确实有两个(小?)问题。首先,这意味着我所有的本机 C++ 客户端访问头文件的方式都有些笨拙。其次,本机 C++ 客户端在枚举上不会有统一的“公司名称”命名空间前缀。
  • 也许我可以通过 (1) 为 C++ 客户端提供“头文件包装器”和 (2) 我变得邪恶并声明其中一个嵌套命名空间是一个宏来解决“两个小问题” “header wrapper”,因此编译时它将是一个不同的“子”命名空间,而不是不同的“父”命名空间。
  • @Ben:如果您将帖子扩展为“......或保留根/父名称空间,将枚举本身嵌入名称空间层次结构中,我会将您的帖子标记为答案,并且将其中一个子命名空间定义为宏 - 当 #included 作为本机声明时。实际上,您将修改子命名空间,而不是添加新的最顶层命名空间层。基本上,这总结了讨论并实现了我的命名空间约定和 x86/x64 部署要求的目标。
猜你喜欢
  • 2023-02-20
  • 1970-01-01
  • 1970-01-01
  • 2012-01-10
  • 2016-06-27
  • 1970-01-01
  • 2010-09-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多