【问题标题】:How can I process C++ definitions in C#?如何在 C# 中处理 C++ 定义?
【发布时间】:2011-10-06 05:31:13
【问题描述】:

我有一堆 *.h 文件,只包含 c 风格的定义,如

#define ALPHA_REACTOR_CODE 99641
#define BETA_REACTOR_CODE  99642
#define GAMMA_REACTOR_CODE 99643
#define DELTA_REACTOR_CODE 99644

如何在我的 С# 代码中使用此文件而不进行更改并在我的代码中使用此常量?

【问题讨论】:

  • 您需要处理这些声明以生成 C# 等效项。
  • 你是说我需要解析器来生成常量列表吗?
  • @kandreych:你需要这个作为设计时间吗?还是只是运行时?
  • @AlastairPitts 它是 2 个不同项目的通用文件,所以我不想复制它们,因为将来可能会导致一些同步错误
  • 我的意思是,你需要在设计时使用这个常量吗?或者运行时可以吗?另外,它们总是整数值吗?

标签: c# c++ constants


【解决方案1】:

抱歉,我早先答应了,但回答迟了。

我的想法涉及使用运行时代码编译器以及反射来动态生成包含所有这些常量的类。

所以我看到的步骤(或伪代码)是:

  1. 对于每个 .h 文件,读取所有以 #define 开头的行并存储这些行。
  2. 使用Microsoft.CSharp.CSharpCodeProvider() 和一个空模板文件,在内存中编译一个DLL,其中包含一个公开public const int ALPHA_REACTOR_CODE = 99641; 样式常量的类。
  3. 使用Assembly.GetType(string) 获取生成的类的类型
  4. 使用反射和采用常量名称的方法来访问值。

如果这是我们的攻击计划,这里有一些可能对您有所帮助的 sn-ps 代码。

1.加载 .h 文件 这是非常基本的文件读取,关键技巧是通过#define 过滤您的行,然后解析为Dictionary<string, int>

lines.Where(line => line.StartsWith("#define", true, CultureInfo.CurrentCulture))
                .Select(line => line.Split(' ').Skip(1))
                .ToDictionary(lineParts => lineParts.First(), lineParts => int.Parse(lineParts.Last()));

这将生成一个包含所有定义的字典。您可能需要在.ToDictionary() 之前调用.Distict(),以防有重复定义。

2。生成程序集 这是您使用Microsoft.CSharp.CSharpCodeProvider() 生成内存中程序集的地方。这里的诀窍是有一个“GenerateCode.cs”文件,其中包含一个类的基本结构。这必须设置为 EmbeddedResource,否则它会尝试编译(不起作用)。

using System;

namespace GeneratedCode
{{
    public class GeneratedConstants
    {{

{0}

    }}
}}

您会注意到{0},我们使用它和string.Format 将常量插入文件中。

使用assembly.GetManifestResourceStream() 获取此模板文件的流,使用普通StreamReader 将其读入内存中的一个大字符串。

使用StringBuilder 和基本string.Format() 格式以及值字典生成常量。

    StringBuilder builder = new StringBuilder();
    foreach (var constant in dict)
    {
        builder.AppendFormat(CultureInfo.CurrentCulture, "public const int {0} = {1};", constant.Key, constant.Value);
    }

然后只需使用 string.Format() 将构建器字符串插入到生成的类字符串中(使用{0}

然后,您只需要使用Microsoft.CSharp.CSharpCodeProvider() 在内存中生成一个程序集。

 using (CSharpCodeProvider provider = new CSharpCodeProvider())
            {
                parameters.ReferencedAssemblies.Add("System.dll");
                parameters.ReferencedAssemblies.Add("System.Core.dll");

                CompilerResults results = provider.CompileAssemblyFromSource(parameters, sourceCode);

            }

3 & 4. 使用反射 现在你有一个程序集,其中包含一个类型,其中定义了你的常量,你可以使用assembly.GetType("GeneratedCode.GeneratedConstants"); 来获取类型,并使用反射来获取常量,一次一个或一起。

查看这篇博客文章,其中介绍了如何使用反射获取 const:http://weblogs.asp.net/whaggard/archive/2003/02/20/2708.aspx

嗯,希望对你有所帮助。这不是一个过于复杂的解决方案,但有一些活动部件。

祝你好运!

【讨论】:

  • 这个解决方案似乎太完美了。顺便说一句,正如您所说,必须将 GenerateCode.cs 设置为嵌入式资源。我想源 *.h 文件也应该嵌入,否则它们必须一直在附近的某个地方 exe?
  • @kandreych:由你决定,我个人会将它们作为嵌入式资源,这样你就可以保证它们在编译后就在那里。
【解决方案2】:

编写一个程序,将 C 风格的 .h 文件转换为 C# 等效文件。如果如声称的那样,.h 文件仅包含 int #defines,那么这在您选择的语言中应该是非常简单的。

理想情况下,转换将正确集成到您的构建/制作过程中,以便在/如果 .h 文件发生更改时重做。

【讨论】:

  • +1 在 perl、python 等中这样做非常简单。而且非常有效
【解决方案3】:

我不知道你想要的是否可能。然而,这里值得的是 C# 方式:

C# 不像 c++ 那样支持#define。我认为您必须将这些常量写为枚举或常量值。例如

enum REACTOR
{
    ALPHA = 99641,
    BETA = 99642,
    GAMMA = 99643,
    DELTA = 99641
}

或使用常量,因为这些值在编译时被替换

const int ALPHA_REACTOR = 99641;

如果你有很多这些值(一个 .h 类),我想你可以创建一个“常量”容器类,它将所有这些值保存在单独的枚举中,描述里面的值(REACTOR、X、Y、Z)所以您可以简单地在代码中调用 Constants.REACTOR.ALPHA。

喜欢这篇文章,因为我对这个问题的实际答案很感兴趣。我想知道是否有可能你想做的事情。

//编辑 This link 建议我在下面引用的另一个选项。

如果您愿意,您可能还想探索一条替代路径: 创建一个包含所有常量的程序集,从 你的程序集,然后编译。常量值将被复制到 你的代码,因为常量的程序集永远不会被使用,它 永远不会被寻找,让您根本不分发它。

我只会在一些非常广泛的测试后推荐这个,因为我是 不确定所有 JIT 实现是否都允许这样做。

不一样,而是另一种选择,生成额外的 C# 代码。

【讨论】:

  • 好吧,我实际上并不想修改 *.h 文件——它们是从某种只读源中包含的。
  • @kandreych:不要修改头文件,创建新的 C# 文件。你可以生成它们。唯一的其他选择是不使用 C# 而是使用(托管)C++。
  • @bastijn 程序集每次 *.h 文件更改时都需要重新编译,所以这不是我认为的最佳方式
【解决方案4】:

也许您希望托管 C++ 将您的 C 代码与 C# 代码集成?

【讨论】:

    猜你喜欢
    • 2021-08-30
    • 2010-12-15
    • 1970-01-01
    • 2018-06-06
    • 2011-07-09
    • 1970-01-01
    • 1970-01-01
    • 2021-10-17
    • 1970-01-01
    相关资源
    最近更新 更多