문제

#define 지시문에 버전 번호가 있는 헤더 파일이 포함된 C++ 프로젝트(VS2005)가 있습니다.이제 트윈 C# 프로젝트에 정확히 동일한 번호를 포함해야 합니다.가장 좋은 방법은 무엇입니까?

이 파일을 리소스로 포함시킨 다음 정규식을 사용하여 런타임에 구문 분석하여 버전 번호를 복구하려고 합니다. 하지만 더 좋은 방법이 있을 수도 있습니다. 어떻게 생각하시나요?

버전을 .h 파일 외부로 이동할 수 없으며 빌드 시스템도 이에 따라 달라지며 C# 프로젝트는 조정되어야 합니다.

도움이 되었습니까?

해결책

다음과 같은 몇 단계만 거치면 원하는 것을 얻을 수 있습니다.

  1. MSBuild 작업 생성 - http://msdn.microsoft.com/en-us/library/t9883dzc.aspx
  2. 빌드 전에 생성된 작업에 대한 호출을 포함하도록 프로젝트 파일을 업데이트합니다.

작업은 참조한 헤더 .h 파일의 위치가 포함된 매개변수를 받습니다.그런 다음 버전을 추출하고 이전에 만든 C# 자리 표시자 파일에 해당 버전을 넣습니다.또는 괜찮다면 일반적으로 버전을 보유하는 AssemblyInfo.cs를 사용해도 됩니다.

추가 정보가 필요하시면 언제든지 댓글을 남겨주세요.

다른 팁

.h를 처리하고 .cs 파일로 변환하려면 .tt 파일을 사용하는 것이 좋습니다.매우 쉽고 소스 파일은 C# 솔루션의 일부가 되며(즉, .h 파일이 변경되면 새로 고쳐짐) 클릭하여 편집기에서 열 수 있습니다.

#define이 1개만 있는 경우 약간 과잉일 수 있지만 이러한 항목으로 가득 찬 파일(예: mfc resources.h 파일)이 있는 경우 이 솔루션이 큰 승리가 됩니다.

예:DefineConverter.tt 파일을 생성하여 프로젝트에 추가하고 표시된 줄을 .h 파일을 참조하도록 변경하면 정적 const 항목으로 가득 찬 프로젝트에 새 클래스가 생성됩니다.(입력 파일은 프로젝트 파일에 상대적입니다. 절대 경로를 원하는 경우 호스트별=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]#>";
<#
            }
        }
    } #> 
    }
}

MSDN은 다음과 같이 말합니다.

#Define 지시문은 일반적으로 C 및 C ++에서 수행되는 상수 값을 선언하는 데 사용할 수 없습니다.C#의 상수는 클래스 또는 구조물의 정적 멤버로 가장 잘 정의됩니다.그러한 상수가 여러 개있는 경우 별도의 "상수"클래스를 작성하여 고정하십시오.

상수 주변의 래퍼인 클래스를 포함하는 관리형 C++를 사용하여 라이브러리를 만들 수 있습니다.그런 다음 C# 프로젝트에서 이 클래스를 참조할 수 있습니다.그냥 사용하는 것을 잊지 마세요 읽기 전용 < 유형 > 대신에 const <유형> 상수 선언을 위해 :)

언제든지 사전 빌드 이벤트를 사용하여 .cs 파일에서 C 전처리기를 실행하고 사후 빌드 이벤트를 사용하여 사전 빌드 단계를 실행 취소할 수 있습니다.전처리기는 단지 텍스트 대체 시스템이므로 다음이 가능합니다.

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

이 .h 파일을 포함하는 간단한 C++/C 유틸리티를 작성하고 C#에서 사용할 수 있는 파일을 동적으로 생성할 수 있습니다.
이 유틸리티는 사전 빌드 단계로 C# 프로젝트의 일부로 실행될 수 있습니다.
이렇게 하면 항상 원본 파일과 동기화됩니다.

저는 #define FOO "bar"를 C#에서 사용할 수 있는 것으로 변환하는 Python 스크립트를 작성했으며 이를 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'

gbjbaanb의 솔루션을 기반으로 특정 디렉터리에서 모든 .h 파일을 찾아 여러 클래스가 있는 .cs 파일로 롤업하는 .tt 파일을 만들었습니다.

차이점

  • 복식 지원을 추가했습니다.
  • try-catch에서 TryParse로 전환됨
  • 여러 .h 파일을 읽습니다.
  • 'const' 대신 'readonly'를 사용합니다.
  • 다음으로 끝나는 #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]#>";
<#          
            }
        }
    } #>
    }
<#}#>     
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top