Повторное использование определения оператора из файла .h в коде C # - PullRequest
6 голосов
/ 19 сентября 2008

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

Я подумываю включить этот файл в качестве ресурса, а затем проанализировать его во время выполнения с помощью регулярного выражения для восстановления номера версии, но, возможно, есть лучший способ, как вы думаете?

Я не могу переместить версию за пределы файла .h, также от нее зависит система сборки, и проект C # должен быть адаптирован.

Ответы [ 7 ]

5 голосов
/ 12 апреля 2011

Я хотел бы рассмотреть возможность использования файла .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]#>";
<#
            }
        }
    } #> 
    }
}
3 голосов
/ 19 сентября 2008

MSDN сообщает нам:

Директива #define не может быть использована объявить постоянные значения как есть обычно делается на C и C ++. Константы в C # лучше всего определить как статические члены класса или структуры. если ты есть несколько таких констант, рассмотрим создание отдельного класса "Константы" держать их.

Вы можете создать библиотеку, используя управляемый C ++, который включает в себя оболочку класса вокруг ваших констант. Затем вы можете ссылаться на этот класс из проекта C #. Только не забудьте использовать readonly вместо const для объявления ваших констант:)

2 голосов
/ 19 сентября 2008

Вы можете достичь желаемого за несколько шагов:

  1. Создание задачи MSBuild - http://msdn.microsoft.com/en-us/library/t9883dzc.aspx
  2. Обновите файл проекта, включив в него вызов задачи, созданной до сборки

Задача получает параметр с указанием местоположения указанного вами заголовочного файла .h. Затем он извлекает версию и помещает ее в файл-заполнитель C #, который вы ранее создали. Или вы можете подумать об использовании AssemblyInfo.cs, который обычно содержит версии, если это вам подходит.

Если вам нужна дополнительная информация, пожалуйста, не стесняйтесь комментировать.

2 голосов
/ 19 сентября 2008

Вы всегда можете использовать событие pre-build для запуска препроцессора C в файле .cs и событие post build для отмены шага pre-build. Препроцессор - это просто система подстановки текста, так что это возможно:

// 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";
1 голос
/ 19 сентября 2008

Вы можете написать простую утилиту для C ++ / C, которая включает этот файл .h, и динамически создавать файл, который можно использовать в C #.
Эта утилита может быть запущена как часть проекта C # на этапе предварительной сборки.
Таким образом, вы всегда синхронизируете с исходным файлом.

0 голосов
/ 05 апреля 2016

Основываясь на решении gbjbaanb, я создал файл .tt, который находит все файлы .h в определенном каталоге и объединяет их в файл .cs с несколькими классами.

Отличия

  • Я добавил поддержку для двойников
  • Переключен с try-catch на TryParse
  • Читает несколько файлов .h
  • Используется «только для чтения» вместо «const»
  • Обрезает # определяет строки, оканчивающиеся на ;
  • Пространство имен устанавливается на основе местоположения .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]#>";
<#          
            }
        }
    } #>
    }
<#}#>     
}
0 голосов
/ 31 октября 2008

Я написал скрипт на python, который преобразует "бар" #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'
...