У меня есть проект C++ (VS2005), который включает файл заголовка с номером версии в директиве #define. Теперь мне нужно включить точно такое же число в двойной проект C#. Как лучше всего это сделать?
Я подумываю включить этот файл в качестве ресурса, а затем проанализировать его во время выполнения с помощью регулярного выражения, чтобы восстановить номер версии, но, может быть, есть способ лучше, как вы думаете?
Я не могу переместить версию за пределы файла .h, от нее также зависит система сборки, а проект C# - это тот, который следует адаптировать.





MSDN сообщает нам:
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.
Вы можете создать библиотеку, используя управляемый C++, который включает класс-оболочку для ваших констант. Затем вы можете ссылаться на этот класс из проекта C#. Только не забудьте использовать только для чтения <тип> вместо const <тип> для объявления констант :)
Вы всегда можете использовать событие перед сборкой, чтобы запустить препроцессор C для файла .cs, и событие после сборки, чтобы отменить шаг перед сборкой. Препроцессор - это просто система подстановки текста, поэтому это возможно:
// 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, который обычно содержит версии, если вам это подходит.
Если вам нужна дополнительная информация, пожалуйста, оставьте комментарий.
Вы можете написать простую утилиту C++ / C, которая включает этот файл .h и динамически создает файл, который можно использовать в C# .
Эту утилиту можно запустить как часть проекта C# на этапе предварительной сборки.
Таким образом вы всегда синхронизируетесь с исходным файлом.
Я написал сценарий python, который преобразует "bar" #define FOO в нечто, что можно использовать на 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'
Я бы подумал об использовании файла .tt для обработки .h и превращения его в файл .cs. Это очень просто, и исходные файлы станут частью вашего решения C# (то есть они будут обновляться по мере изменения файла .h), их можно будет щелкнуть, чтобы открыть в редакторе, и т. д.
Если у вас есть только 1 #define, это может быть немного излишним, но если у вас есть файл, полный их (например, файл mfc resource.h, возможно), тогда это решение станет большой победой.
например: создайте файл DefineConverter.tt и добавьте его в свой проект, измените отмеченную строку, чтобы она ссылалась на ваш .h файл, и вы получите новый класс в своем проекте, полный статических константных записей. (обратите внимание, что входной файл относится к файлу вашего проекта, установите 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]#>";
<#
}
}
} #>
}
}
Основываясь на решении 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]#>";
<#
}
}
} #>
}
<#}#>
}
Мне это кажется наиболее очевидным и прямым ответом.