2008-09-19 15 views
6

Tengo un proyecto de C++ (VS2005) que incluye el archivo de encabezado con el número de versión en la directiva #define. Ahora necesito incluir exactamente el mismo número en el proyecto gemelo de C#. ¿Cuál es la mejor manera de hacerlo?Declaración de definición de reutilización del archivo .h en el código de C#

Estoy pensando en incluir este archivo como recurso, luego analizarlo en tiempo de ejecución con regex para recuperar el número de versión, pero tal vez haya una mejor manera, ¿qué opinas?

No puedo mover la versión fuera del archivo .h, también el sistema de compilación depende de ello y el proyecto C# es uno que debe adaptarse.

Respuesta

2

se puede lograr lo que quiere en pocos pasos:

  1. crear una tarea de MSBuild - http://msdn.microsoft.com/en-us/library/t9883dzc.aspx
  2. actualización del archivo de proyecto para incluir una llamada a la tarea creada antes de construir

La tarea recibe un parámetro con la ubicación del archivo .h de encabezado que ha mencionado. A continuación, extrae la versión y la coloca en un archivo de marcador de posición C# que haya creado previamente. O puede pensar en usar AssemblyInfo.cs que normalmente contiene versiones si eso está bien para usted.

Si necesita información adicional, no dude en comentar.

3

MSDN nos dice:

La directiva #define no se puede utilizar para declarar valores constantes como es normalmente hecho en C y C++. Las constantes en C# se definen mejor como miembros estáticos de una clase o estructura. Si tiene tiene varias de tales constantes, considere creando una clase separada de "Constantes" para mantenerlas.

Puede crear una biblioteca utilizando C++ administrado que incluya un contenedor de clase alrededor de sus constantes. Entonces puede hacer referencia a esta clase desde el proyecto C#. Pero no se olvide de usar sólo lectura < tipo> en lugar de const < tipo> para su declaración de constantes :)

2

Siempre se puede utilizar el evento de pre-construcción para ejecutar el preprocesador de C en el archivo .cs y el evento de compilación posterior para deshacer el paso de preconstrucción. El preprocesador es sólo un sistema de texto de sustitución, así que esto es posible:

// version header file 
#define Version "1.01" 

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

y el preprocesador generará:

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

Esto sólo parece la respuesta más obvia y directa a mí. –

1

Puede escribir la utilidad simple de C++/C que incluye este archivo .h y crear dinámicamente el archivo que se puede usar en C#.
Esta utilidad se puede ejecutar como una parte del proyecto C# como una etapa de preconstrucción.
De esta forma siempre estará sincronizado con el archivo original.

0

Escribí un script de python que convierte #define FOO "bar" en algo utilizable en C# y lo estoy usando en un paso previo a la construcción en mi proyecto C#. Funciona.

# 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' 
5

Considero utilizar un archivo .tt para procesar el .h y convertirlo en un archivo .cs. Es muy fácil y los archivos fuente serán parte de su solución C# (lo que significa que se actualizarán cuando el archivo .h cambie), se puede hacer clic para abrir en el editor, etc.

Si solo tiene tiene 1 #define puede ser un poco exagerado, pero si tiene un archivo lleno de ellos (por ejemplo, un archivo mfc resource.h quizás), entonces esta solución se convierte en una gran victoria.

por ejemplo: cree un archivo, DefineConverter.tt y agréguelo a su proyecto, cambie la línea marcada para hacer referencia a su archivo .h, y obtendrá una nueva clase en su proyecto llena de entradas estáticas const. (tenga en cuenta que el archivo de entrada es relativo a su archivo de proyecto, establezca hostspecific = false si desea rutas absolutas).

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

Sobre la base de la solución de gbjbaanb, he creado un archivo .tt que encuentra todos los archivos .h en un directorio específico y los rollos en un archivo .cs con múltiples clases.

Diferencias

  • que añaden soporte para los dobles
  • cambiaron de try-catch a TryParse
  • Reads varios archivos .h 'sólo lectura'
  • usos en lugar de 'const'
  • recorta #define líneas que terminan en ;
  • espacio de nombres se establece en función de la ubicación del proyecto en .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]#>"; 
<#   
      } 
     } 
    } #> 
    } 
<#}#>  
} 
Cuestiones relacionadas