Повторное использование оператора define из файла .h в коде C#

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

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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
0
10 800
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

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";

Мне это кажется наиболее очевидным и прямым ответом.

Fabio A. 09.05.2015 09:38
Ответ принят как подходящий

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

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

Задача получает параметр с расположением указанного вами заголовочного файла .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 с несколькими классами.

Отличия

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

Другие вопросы по теме