在 C# 代码中重用 .h 文件中的定义语句

发布于 2024-07-04 17:23:25 字数 215 浏览 7 评论 0原文

我有 C++ 项目(VS2005),其中包含 #define 指令中带有版本号的头文件。 现在我需要在孪生 C# 项目中包含完全相同的数字。 最好的方法是什么?

我正在考虑将此文件作为资源包含在内,然后在运行时使用正则表达式解析它以恢复版本号,但也许有更好的方法,您认为呢?

我无法将版本移到 .h 文件之外,构建系统也依赖于它,并且 C# 项目是应该进行调整的项目。

I have C++ project (VS2005) which includes header file with version number in #define directive. Now I need to include exactly the same number in twin C# project. What is the best way to do it?

I'm thinking about including this file as a resource, then parse it at a runtime with regex to recover version number, but maybe there's a better way, what do you think?

I cannot move version outside .h file, also build system depends on it and the C# project is one which should be adapted.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

霓裳挽歌倾城醉 2024-07-11 17:23:25

您始终可以使用预构建事件在 .cs 文件上运行 C 预处理器,并使用构建后事件来撤消预构建步骤。 预处理器只是一个文本替换系统,所以这是可能的:

// version header file
#define Version "1.01"

// C# code
#include "version.h"
// somewhere in a class
string version = Version;

并且预处理器将生成:

// C# code
// somewhere in a class
string version = "1.01";

You could always use the pre-build event to run the C preprocessor on the .cs file and the post build event to undo the pre-build step. The preprocessor is just a text-substitution system, so this is possible:

// version header file
#define Version "1.01"

// C# code
#include "version.h"
// somewhere in a class
string version = Version;

and the preprocessor will generate:

// C# code
// somewhere in a class
string version = "1.01";
心舞飞扬 2024-07-11 17:23:25

您可以编写包含此 .h 文件的简单 C++/C 实用程序,并动态创建可在 C# 中使用的文件。
该实用程序可以作为 C# 项目的一部分作为预构建阶段运行。
这样您始终与原始文件同步。

You can write simple C++/C utility that include this .h file and dynamically create file that can be used in C#.
This utility can be run as a part of C# project as a pre-build stage.
This way you are always sync with the original file.

沙沙粒小 2024-07-11 17:23:25

MSDN 告诉我们:

无法使用#define指令
按原样声明常量值
通常用 C 和 C++ 完成。 常数
在 C# 中最好定义为静态
类或结构的成员。 如果你
有几个这样的常数,考虑
创建一个单独的“常量”类
来抓住他们。

您可以使用托管 C++ 创建库,其中包含围绕常量的类包装器。 然后您可以从 C# 项目引用此类。 只是不要忘记使用 readonly <; 输入 > 而不是 const < 输入 > 作为常量声明:)

MSDN tells us:

The #define directive cannot be used
to declare constant values as is
typically done in C and C++. Constants
in C# are best defined as static
members of a class or struct. If you
have several such constants, consider
creating a separate "Constants" class
to hold them.

You can create library using managed C++ that includes class - wrapper around your constants. Then you can reference this class from C# project. Just don't forget to use readonly < type > instead of const < type > for your constants declaration :)

一直在等你来 2024-07-11 17:23:25

只需几个步骤即可实现您想要的目标:

  1. 创建 MSBuild 任务 - http ://msdn.microsoft.com/en-us/library/t9883dzc.aspx
  2. 更新项目文件以包含对构建之前创建的任务的调用

该任务接收带有标头 .h 位置的参数您提到的文件。 然后,它提取该版本并将该版本放入您之前创建的 C# 占位符文件中。 或者,如果您可以的话,您可以考虑使用通常保存版本的 AssemblyInfo.cs。

如果您需要更多信息,请随时发表评论。

You can achieve what you want in just a few steps:

  1. Create a MSBuild Task - http://msdn.microsoft.com/en-us/library/t9883dzc.aspx
  2. Update the project file to include a call to the task created prior to build

The task receives a parameter with the location of the header .h file you referred. It then extracts the version and put that version in a C# placeholder file you previously have created. Or you can think using AssemblyInfo.cs that normally holds versions if that is ok for you.

If you need extra information please feel free to comment.

旧人哭 2024-07-11 17:23:25

我编写了一个 python 脚本,它将 #define FOO“bar” 转换为 C# 中可用的内容,并且我在 C# 项目的预构建步骤中使用它。 有用。

# translate the #defines in messages.h file into consts in MessagesDotH.cs

import re
import os
import stat

def convert_h_to_cs(fin, fout):
    for line in fin:
        m = re.match(r"^#define (.*) \"(.*)\"", line)
        if m != None:
            if m.group() != None:
                fout.write( "public const string " \
                + m.group(1) \
                + " = \"" \
                + m.group(2) \
                + "\";\n" )
        if re.match(r"^//", line) != None:
            fout.write(line)

fin = open ('..\common_cpp\messages.h')
fout = open ('..\user_setup\MessagesDotH.cs.tmp','w')

fout.write( 'using System;\n' )
fout.write( 'namespace xrisk { class MessagesDotH {\n' )

convert_h_to_cs(fin, fout)

fout.write( '}}' )

fout.close()

s1 = open('..\user_setup\MessagesDotH.cs.tmp').read()

s2 = open('..\user_setup\MessagesDotH.cs').read()

if s1 != s2:
    os.chmod('..\user_setup\MessagesDotH.cs', stat.S_IWRITE)
    print 'deleting old MessagesDotH.cs'
    os.remove('..\user_setup\MessagesDotH.cs')
    print 'remaming tmp to MessagesDotH.cs'
    os.rename('..\user_setup\MessagesDotH.cs.tmp','..\user_setup\MessagesDotH.cs')
else:
    print 'no differences.  using same MessagesDotH.cs'

I wrote a python script that converts #define FOO "bar" into something usable in C# and I'm using it in a pre-build step in my C# project. It works.

# translate the #defines in messages.h file into consts in MessagesDotH.cs

import re
import os
import stat

def convert_h_to_cs(fin, fout):
    for line in fin:
        m = re.match(r"^#define (.*) \"(.*)\"", line)
        if m != None:
            if m.group() != None:
                fout.write( "public const string " \
                + m.group(1) \
                + " = \"" \
                + m.group(2) \
                + "\";\n" )
        if re.match(r"^//", line) != None:
            fout.write(line)

fin = open ('..\common_cpp\messages.h')
fout = open ('..\user_setup\MessagesDotH.cs.tmp','w')

fout.write( 'using System;\n' )
fout.write( 'namespace xrisk { class MessagesDotH {\n' )

convert_h_to_cs(fin, fout)

fout.write( '}}' )

fout.close()

s1 = open('..\user_setup\MessagesDotH.cs.tmp').read()

s2 = open('..\user_setup\MessagesDotH.cs').read()

if s1 != s2:
    os.chmod('..\user_setup\MessagesDotH.cs', stat.S_IWRITE)
    print 'deleting old MessagesDotH.cs'
    os.remove('..\user_setup\MessagesDotH.cs')
    print 'remaming tmp to MessagesDotH.cs'
    os.rename('..\user_setup\MessagesDotH.cs.tmp','..\user_setup\MessagesDotH.cs')
else:
    print 'no differences.  using same MessagesDotH.cs'
明媚殇 2024-07-11 17:23:25

在 gbjbaanb 的解决方案的基础上,我创建了一个 .tt 文件,该文件查找特定目录中的所有 .h 文件,并将它们滚动到具有多个类的 .cs 文件中。

差异

  • 我添加了对双精度的支持
  • 从 try-catch 切换到 TryParse
  • 读取多个 .h 文件
  • 使用“readonly”而不是“const”
  • 修剪以 ; 结尾的 #define 行
  • 命名空间是根据 .tt 位置设置的在项目中

<#@ template language="C#" hostspecific="True" debug="True" #>
<#@ output extension="cs" #>
<#@ assembly name="System.Core.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#
string hPath = Host.ResolveAssemblyReference("$(ProjectDir)") + "ProgramData\\DeltaTau\\";  
string[] hFiles = System.IO.Directory.GetFiles(hPath, "*.h", System.IO.SearchOption.AllDirectories);
var namespaceName = System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint");
#>
//------------------------------------------------------------------------------
//     This code was generated by template for T4
//     Generated at <#=DateTime.Now#>
//------------------------------------------------------------------------------

namespace <#=namespaceName#>
{
<#foreach (string input_file in hFiles)
{
StreamReader defines = new StreamReader(input_file);
#>
    public class <#=System.IO.Path.GetFileNameWithoutExtension(input_file)#>
    {
<#    // constants definitions

    while (defines.Peek() >= 0)
    {
        string def = defines.ReadLine();
        string[] parts;
        if (def.Length > 3 && def.StartsWith("#define"))
        {
            def = def.TrimEnd(';');
            parts = def.Split(null as char[], StringSplitOptions.RemoveEmptyEntries);
            Int32 intVal;
            double dblVal;
            if (Int32.TryParse(parts[2], out intVal))
            {
            #>
        public static readonly int <#=parts[1]#> = <#=parts[2]#>;           
<#
            }
            else if (Double.TryParse(parts[2], out dblVal))
            {
            #>
        public static readonly double <#=parts[1]#> = <#=parts[2]#>;            
<#
            }
            else
            {
            #>
        public static readonly string <#=parts[1]#> = "<#=parts[2]#>";
<#          
            }
        }
    } #>
    }
<#}#>     
}

Building on gbjbaanb's solution, I created a .tt file that finds all .h files in a specific directory and rolls them into a .cs file with multiple classes.

Differences

  • I added support for doubles
  • Switched from try-catch to TryParse
  • Reads multiple .h files
  • Uses 'readonly' instead of 'const'
  • Trims #define lines that end in ;
  • Namespace is set based on .tt location in project

<#@ template language="C#" hostspecific="True" debug="True" #>
<#@ output extension="cs" #>
<#@ assembly name="System.Core.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#
string hPath = Host.ResolveAssemblyReference("$(ProjectDir)") + "ProgramData\\DeltaTau\\";  
string[] hFiles = System.IO.Directory.GetFiles(hPath, "*.h", System.IO.SearchOption.AllDirectories);
var namespaceName = System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint");
#>
//------------------------------------------------------------------------------
//     This code was generated by template for T4
//     Generated at <#=DateTime.Now#>
//------------------------------------------------------------------------------

namespace <#=namespaceName#>
{
<#foreach (string input_file in hFiles)
{
StreamReader defines = new StreamReader(input_file);
#>
    public class <#=System.IO.Path.GetFileNameWithoutExtension(input_file)#>
    {
<#    // constants definitions

    while (defines.Peek() >= 0)
    {
        string def = defines.ReadLine();
        string[] parts;
        if (def.Length > 3 && def.StartsWith("#define"))
        {
            def = def.TrimEnd(';');
            parts = def.Split(null as char[], StringSplitOptions.RemoveEmptyEntries);
            Int32 intVal;
            double dblVal;
            if (Int32.TryParse(parts[2], out intVal))
            {
            #>
        public static readonly int <#=parts[1]#> = <#=parts[2]#>;           
<#
            }
            else if (Double.TryParse(parts[2], out dblVal))
            {
            #>
        public static readonly double <#=parts[1]#> = <#=parts[2]#>;            
<#
            }
            else
            {
            #>
        public static readonly string <#=parts[1]#> = "<#=parts[2]#>";
<#          
            }
        }
    } #>
    }
<#}#>     
}
苏佲洛 2024-07-11 17:23:25

我会考虑使用 .tt 文件来处理 .h 并将其转换为 .cs 文件。 这非常简单,源文件将成为 C# 解决方案的一部分(这意味着它们将随着 .h 文件的更改而刷新),可以单击在编辑器中打开,等等。

如果您只有 1 个 #define这可能有点矫枉过正,但如果您有一个充满它们的文件(例如,也许是 mfc resources.h 文件),那么这个解决方案将成为一个巨大的胜利。

例如:创建一个文件 DefineConverter.tt 并将其添加到您的项目中,更改标记行以引用您的 .h 文件,然后您将在项目中获得一个充满静态 const 条目的新类。 (请注意,输入文件是相对于您的项目文件的,如果您想要绝对路径,请设置 hostspecic=false)。

<#@ template language="C#v3.5" hostspecific="True" debug="True" #>
<#@ output extension="cs" #>
<#@ assembly name="System.Core.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>

<#
string input_file = this.Host.ResolvePath("resource.h");             <---- change this
StreamReader defines = new StreamReader(input_file);
#>
//------------------------------------------------------------------------------
//     This code was generated by template for T4
//     Generated at <#=DateTime.Now#>
//------------------------------------------------------------------------------

namespace Constants
{
    public class <#=System.IO.Path.GetFileNameWithoutExtension(input_file)#>
    {
<#
    // constants definitions

    while (defines.Peek() >= 0)
    {
        string def = defines.ReadLine();
        string[] parts;
        if (def.Length > 3 && def.StartsWith("#define"))
        {
            parts = def.Split(null as char[], StringSplitOptions.RemoveEmptyEntries);
            try {
                Int32 numval = Convert.ToInt32(parts[2]);
                #>
        public static const int <#=parts[1]#> = <#=parts[2]#>;
<#
            }
            catch (FormatException e) {
            #>
        public static const string <#=parts[1]#> = "<#=parts[2]#>";
<#
            }
        }
    } #> 
    }
}

I would consider using a .tt file to process the .h and turn it into a .cs file. Its very easy and the source files will then be part of your C# solution (meaning they will be refreshed as the .h file changes), can be clicked on to open in the editor, etc.

If you've only got 1 #define it might be a little overkill, but if you have a file full of them (eg a mfc resource.h file perhaps) then this solution becomes a big win.

eg: create a file, DefineConverter.tt and add it to your project, change the marked line to refer to your .h file, and you'll get a new class in your project full of static const entries. (note the input file is relative to your project file, set hostspecific=false if you want absolute paths).

<#@ template language="C#v3.5" hostspecific="True" debug="True" #>
<#@ output extension="cs" #>
<#@ assembly name="System.Core.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>

<#
string input_file = this.Host.ResolvePath("resource.h");             <---- change this
StreamReader defines = new StreamReader(input_file);
#>
//------------------------------------------------------------------------------
//     This code was generated by template for T4
//     Generated at <#=DateTime.Now#>
//------------------------------------------------------------------------------

namespace Constants
{
    public class <#=System.IO.Path.GetFileNameWithoutExtension(input_file)#>
    {
<#
    // constants definitions

    while (defines.Peek() >= 0)
    {
        string def = defines.ReadLine();
        string[] parts;
        if (def.Length > 3 && def.StartsWith("#define"))
        {
            parts = def.Split(null as char[], StringSplitOptions.RemoveEmptyEntries);
            try {
                Int32 numval = Convert.ToInt32(parts[2]);
                #>
        public static const int <#=parts[1]#> = <#=parts[2]#>;
<#
            }
            catch (FormatException e) {
            #>
        public static const string <#=parts[1]#> = "<#=parts[2]#>";
<#
            }
        }
    } #> 
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文