我有 C++ 项目(VS2005),它在#define 指令中包含带有版本号的头文件。现在我需要在双 C# 项目中包含完全相同的数字。最好的方法是什么?
我正在考虑将此文件作为资源包含在内,然后在运行时使用正则表达式对其进行解析以恢复版本号,但也许有更好的方法,你觉得呢?
我不能将版本移到 .h 文件之外,构建系统也依赖于它,而 C# 项目是一个应该适应的项目。
我有 C++ 项目(VS2005),它在#define 指令中包含带有版本号的头文件。现在我需要在双 C# 项目中包含完全相同的数字。最好的方法是什么?
我正在考虑将此文件作为资源包含在内,然后在运行时使用正则表达式对其进行解析以恢复版本号,但也许有更好的方法,你觉得呢?
我不能将版本移到 .h 文件之外,构建系统也依赖于它,而 C# 项目是一个应该适应的项目。
我会考虑使用 .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]#>";
<#
}
}
} #>
}
}
MSDN 告诉我们:
#define 指令不能像 C 和 C++ 中通常那样用于声明常量值。C# 中的常量最好定义为类或结构的静态成员。如果您有多个这样的常量,请考虑创建一个单独的“常量”类来保存它们。
您可以使用包含类的托管 C++ 创建库 - 围绕您的常量进行包装。然后你可以从 C# 项目中引用这个类。只是不要忘记在常量声明中使用readonly < type >而不是const < type > :)
您始终可以使用预构建事件在 .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";
您只需几个步骤即可实现您想要的:
该任务接收一个参数,其中包含您引用的头 .h 文件的位置。然后它会提取版本并将该版本放入您之前创建的 C# 占位符文件中。或者,您可以考虑使用通常包含版本的 AssemblyInfo.cs,如果这对您来说可以的话。
如果您需要更多信息,请随时发表评论。
您可以编写包含此 .h 文件的简单 C++/C 实用程序并动态创建可在 C# 中使用的文件。
该实用程序可以作为 C# 项目的一部分作为预构建阶段运行。
这样,您始终与原始文件同步。
基于 gbjbaanb 的解决方案,我创建了一个 .tt 文件,该文件在特定目录中查找所有 .h 文件,并将它们滚动到具有多个类的 .cs 文件中。
差异
<#@ 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]#>";
<#
}
}
} #>
}
<#}#>
}
我编写了一个 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'