Question

J'ai un projet C ++ (VS2005) qui comprend un fichier d'en-tête avec le numéro de version dans la directive #define. Maintenant, je dois inclure exactement le même nombre dans le projet twin C #. Quelle est la meilleure façon de le faire?

Je pense inclure ce fichier en tant que ressource, puis l'analyser lors d'une exécution avec regex pour récupérer le numéro de version, mais il existe peut-être un meilleur moyen, qu'en pensez-vous?

Je ne peux pas déplacer la version en dehors du fichier .h, le système de construction en dépend également et le projet C # doit être adapté.

Était-ce utile?

La solution

Vous pouvez réaliser ce que vous voulez en quelques étapes seulement:

  1. Créer une tâche MSBuild - http://msdn.microsoft.com/ en-us / library / t9883dzc.aspx
  2. Mettez à jour le fichier de projet pour inclure un appel à la tâche créée avant la construction

La tâche reçoit un paramètre avec l'emplacement du fichier .h d'en-tête que vous avez référencé. Il extrait ensuite la version et la place dans un fichier d'espaces réservés C # que vous avez précédemment créé. Ou vous pouvez penser à utiliser AssemblyInfo.cs qui contient normalement les versions si cela vous convient.

Si vous avez besoin d'informations supplémentaires, n'hésitez pas à commenter.

Autres conseils

J'envisagerais d'utiliser un fichier .tt pour traiter le fichier .h et le transformer en un fichier .cs. C’est très simple et les fichiers sources feront alors partie de votre solution C # (c’est-à-dire qu’ils seront actualisés à mesure que le fichier .h change), vous pouvez cliquer dessus pour les ouvrir dans l’éditeur, etc.

Si vous ne possédez que 1 #define, cela risque d’être un peu exagéré, mais si vous en avez un fichier complet (par exemple, un fichier mfc resource.h peut-être), cette solution devient une grande victoire.

Exemple: créez un fichier, DefineConverter.tt et ajoutez-le à votre projet, modifiez la ligne marquée pour faire référence à votre fichier .h et vous obtiendrez une nouvelle classe dans votre projet complète d'entrées const statiques. (notez que le fichier d'entrée est relatif à votre fichier projet, définissez hostspecific = false si vous voulez des chemins absolus).

<#@ 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 nous dit:

  

La directive #define ne peut pas être utilisée   déclarer des valeurs constantes telles quelles   généralement fait en C et C ++. Constantes   en C # sont mieux définis comme statique   membres d'une classe ou d'une structure. Si vous   avoir plusieurs de ces constantes, considérons   création d'un " Constantes & séparé; classe   pour les tenir.

Vous pouvez créer une bibliothèque à l’aide de C ++ managé, qui inclut une enveloppe de classe autour de vos constantes. Ensuite, vous pouvez référencer cette classe à partir d'un projet C #. N'oubliez pas d'utiliser readonly < tapez > au lieu de const < saisissez > pour votre déclaration de constantes:)

Vous pouvez toujours utiliser l'événement de pré-génération pour exécuter le préprocesseur C sur le fichier .cs et l'événement de post-génération pour annuler l'étape de pré-génération. Le préprocesseur est juste un système de substitution de texte, c'est donc possible:

// version header file
#define Version "1.01"

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

et le préprocesseur générera:

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

Vous pouvez écrire un simple utilitaire C ++ / C incluant ce fichier .h et créer de manière dynamique un fichier pouvant être utilisé en C #.
Cet utilitaire peut être exécuté dans le cadre d'un projet C # en tant qu'étape de pré-génération.
De cette façon, vous êtes toujours synchronisé avec le fichier d'origine.

J'ai écrit un script python qui convertit #define FOO " bar " quelque chose d’utilisable en C # et je l’utilise dans une étape de pré-construction de mon projet C #. Ça marche.

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

En me basant sur la solution de gbjbaanb, j'ai créé un fichier .tt qui recherche tous les fichiers .h dans un répertoire spécifique et les convertit en un fichier .cs à plusieurs classes.

Différences

  • J'ai ajouté le support pour les doubles
  • Passé de try-catch à TryParse
  • Lit plusieurs fichiers .h
  • Utilise 'readonly' au lieu de 'const'
  • Supprime les lignes #define se terminant par ;
  • L'espace de noms est défini en fonction de l'emplacement .tt du projet
<#@ 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]#>";
<#          
            }
        }
    } #>
    }
<#}#>     
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top