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

时间:2008-09-19 10:02:03

标签: c# c++ header c-preprocessor versioning

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

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

我无法移动.h文件以外的版本,构建系统依赖于它,C#项目应该适应。

7 个答案:

答案 0 :(得分:5)

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

如果你只有1 #define它可能有点矫枉过正,但如果你有一个完整的文件(例如mfc resource.h文件),那么这个解决方案将成为一个巨大的胜利。

例如:创建一个文件,DefineConverter.tt并将其添加到您的项目中,更改标记的行以引用您的.h文件,您将在项目中获得一个充满静态const条目的新类。 (注意输入文件是相对于项目文件的,如果你想要绝对路径,则设置hostspecific = 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]#>";
<#
            }
        }
    } #> 
    }
}

答案 1 :(得分:3)

MSDN告诉我们:

  

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

您可以使用托管C ++创建库,其中包含围绕常量的类 - 包装器。然后你可以从C#项目中引用这个类。只是不要忘记使用 readonly&lt;输入&gt; 而不是 const&lt;输入常量声明的类型&gt; :)

答案 2 :(得分:2)

您始终可以使用预构建事件在.cs文件和post构建事件上运行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";

答案 3 :(得分:2)

只需几步即可实现您的目标:

  1. 创建MSBuild任务 - http://msdn.microsoft.com/en-us/library/t9883dzc.aspx
  2. 更新项目文件以包含对构建之前创建的任务的调用
  3. 该任务接收一个参数,其中包含您引用的标题.h文件的位置。然后它提取版本并将该版本放在您之前创建的C#占位符文件中。或者您可以考虑使用通常包含版本的AssemblyInfo.cs,如果可以的话。

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

答案 4 :(得分:1)

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

答案 5 :(得分:0)

我编写了一个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'

答案 6 :(得分:0)

在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]#>";
<#          
            }
        }
    } #>
    }
<#}#>     
}