Pergunta

Eu tenho projeto de C ++ (VS2005) que inclui arquivo de cabeçalho com o número da versão na directiva #define. Agora eu preciso incluir exatamente o mesmo número no projeto gêmeo C #. Qual é a melhor maneira de fazê-lo?

Eu estou pensando em incluir este arquivo como um recurso, em seguida, analisá-lo em um tempo de execução com regex para recuperar o número da versão, mas talvez haja uma maneira melhor, o que você acha?

Eu não posso mover versão arquivo fora .h, também sistema de compilação depende dele eo C # projeto é um que deve ser adaptado.

Foi útil?

Solução

Você pode conseguir o que quiser em apenas alguns passos:

  1. Criar uma tarefa MSBuild - http://msdn.microsoft.com/ en-us / library / t9883dzc.aspx
  2. Atualize o arquivo de projeto para incluir uma chamada para a tarefa criada antes construção

A tarefa recebe um parâmetro com a localização do arquivo de cabeçalho .h que você se refere. Em seguida, ele extrai a versão e colocar essa versão em um arquivo de espaço reservado C # anteriormente criaram. Ou você pode pensar usando AssemblyInfo.cs que normalmente mantém as versões se isso é ok para você.

Se precisar de informação extra sinta-se livre para comentar.

Outras dicas

eu consideraria usando um arquivo .tt para processar o .h e transformá-lo em um arquivo .cs. É muito fácil e os arquivos de origem será, então, parte de sua solução C # (ou seja, eles serão atualizados como as mudanças arquivo .h), pode ser clicado para abrir no editor, etc.

Se você só tem 1 #define que poderia ser um pouco de exagero, mas se você tem um arquivo cheio deles (arquivo por exemplo, um mfc resource.h talvez), então esta solução torna-se uma grande vitória.

por exemplo: criar um arquivo, DefineConverter.tt e adicioná-lo ao seu projeto, alterar a linha marcada para se referir ao seu arquivo .h, e você terá uma nova classe em seu projeto cheio de entradas const estáticas. (Observe o arquivo de entrada é relativo ao seu arquivo de projeto, conjunto hostspecific = false se você quiser caminhos absolutos).

<#@ 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 nos diz:

A directiva #define não pode ser usado para declarar valores constantes como é tipicamente feito em C e C ++. constantes em C # são melhor definida como estática membros de uma classe ou struct. Se vocês têm várias dessas constantes, considere criando um "Constantes" separados classe para mantê-los.

Você pode criar biblioteca usando C ++ gerenciado que inclui classe - invólucro em torno de seus constantes. Em seguida, você pode fazer referência a essa classe de projeto C #. Só não se esqueça de usar somente leitura em vez de const para sua declaração de constantes:)

Você pode sempre usar o evento pré-compilação para executar o pré-processador C na ficheiro.cs eo evento pós compilação para desfazer a etapa de pré-compilação. O pré-processador é apenas um sistema de substituição de texto, então isso é possível:

// version header file
#define Version "1.01"

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

e o pré-processador irá gerar:

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

Você pode escrever ++ / C utilitário simples C que incluir esse arquivo .h e dinamicamente criar arquivo que pode ser usado em C #.
Este utilitário pode ser executado como parte do projeto de C # como um estágio pré-compilação.
Desta forma, você está sempre sincronizado com o arquivo original.

Eu escrevi um script python que converte #define FOO "bar" em algo útil em C # e eu estou usando-o em uma etapa de pré-compilação no meu projeto 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'

Com base na solução de gbjbaanb, eu criei um arquivo .tt que encontra todos os arquivos .h em um diretório específico e rolos los em um arquivo .cs com várias classes.

As diferenças

  • I adicionou suporte para duplas
  • mudado de try-catch para TryParse
  • Lê vários arquivos .h
  • Usos 'readonly' em vez de 'const'
  • apara #define linhas que terminam em ;
  • Namespace é definido com base na localização .tt no projeto

<#@ 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]#>";
<#          
            }
        }
    } #>
    }
<#}#>     
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top