2


C で C++ 標準ライブラリからいくつかのクラスを再作成しようとしています。たとえば、std::pair クラスです。
テンプレートをエミュレートするために、もちろんマクロを使用しました。以下に例を示します。

#define _TEMPLATE_PAIR_STRUCT(T1, T2, TNAME, STRNAME) \
typedef struct {                                  \
    T1* first;                                    \
    T2* second;                                   \
} STRNAME;

#define _TEMPLATE_PAIR_NEW(T1, T2, TNAME, STRNAME) \
    STRNAME* TNAME##_new()                         \
    {                                              \
        STRNAME *new = malloc(sizeof( STRNAME ));  \
        new->first = malloc(sizeof(T1));           \
        new->second = malloc(sizeof(T2));          \
        return new;                                \
    }

この構造を複数のソース ファイルで使用しようとすると、コードを複数回生成する必要があります。それは明らかに、エラーにつながります。

これを修正して、これらの「テンプレート」を C で使用できるようにする方法はありますか?

4

3 に答える 3

5

他の人が言ったように、心に留めておくべきことがいくつかあります。主に、関数定義が 1 つだけであることを確認してください。

私はこのソリューションが特に好きではありませんが、ここにあります。

それらすべてを支配する 1 つのヘッダー (pair.h)

#ifndef TEMPLATE_PAIR
#define TEMPLATE_PAIR

#include <stdlib.h>

#define PAIR_NAME( T1, T2 ) T1##T2##NAME
#define PAIR_PTR_NAME( T1, T2 ) T1##T2##PTR_NAME

#define PAIR_CREATE( T1, T2) MAKE##T1##T2
#define PAIR_PTR_CREATE( T1, T2) MAKE_PTR##T1##T2

#define PAIR_PTR_FREE(T1, T2) FREE##T1##T2

#define PAIR_DEFINITION( T1, T2) \
    typedef struct { \
    T1 first; \
    T2 second ; \
    } PAIR_NAME(T1, T2)

#define PAIR_PTR_DEFINITION( T1, T2) \
    typedef struct { \
    T1* first; \
    T2* second ; \
    } PAIR_PTR_NAME(T1, T2)

#define MAKE_PAIR_DECLARE(T1, T2) PAIR_NAME(T1, T2) PAIR_CREATE(T1, T2) ( const T1& V1, const T2& V2 )
#define MAKE_PAIR_PTR_DECLARE(T1, T2) PAIR_PTR_NAME(T1, T2) PAIR_PTR_CREATE(T1, T2) ( const T1& V1, const T2& V2 )
#define PAIR_PTR_FREE_DECLARE(T1, T2) void PAIR_PTR_FREE(T1, T2) ( PAIR_PTR_NAME(T1, T2) & Pair )

#define MAKE_PAIR_SIGNATURE(T1, T2) PAIR_NAME(T1, T2) PAIR_CREATE(T1, T2) ( const T1& V1, const T2& V2 )
#define MAKE_PAIR_PTR_SIGNATURE(T1, T2) PAIR_PTR_NAME(T1, T2) PAIR_PTR_CREATE(T1, T2) ( const T1& V1, const T2& V2 )

#define FREE_PAIR_PTR_SIGNATURE(T1, T2) void PAIR_PTR_FREE(T1, T2) ( PAIR_PTR_NAME(T1, T2) & Pair )

#define MAKE_PAIR_DEFINE( T1, T2 ) \
    MAKE_PAIR_SIGNATURE(T1, T2) { \
        PAIR_NAME(T1, T2) pair; \
        pair.first = V1; \
        pair.second = V2; \
        return pair; \
        }

#define MAKE_PAIR_PTR_DEFINE( T1, T2 ) \
    MAKE_PAIR_PTR_SIGNATURE(T1, T2) { \
        PAIR_PTR_NAME(T1, T2) pair; \
        pair.first = malloc( sizeof(T1) ); \
        if ( pair.first != 0 ) *(pair.first) = V1; \
        pair.second = malloc( sizeof( T2) ) ; \
        if ( pair.second != 0 ) *(pair.second) = V2; \
        return pair; \
        }

#define PAIR_PTR_FREE_DEFINE( T1, T2 ) \
    FREE_PAIR_PTR_SIGNATURE(T1, T2) { \
    free( Pair.first ); \
    free( Pair.second ); \
    }

#endif

それらすべてをもたらす 1 つのヘッダー (defs.h):

#ifndef DEFS_HEADER
#define DEFS_HEADER

#include "pair.h"

typedef int* pInt;

PAIR_DEFINITION( int, int );
PAIR_DEFINITION( int, double );
PAIR_DEFINITION( double, double );
PAIR_DEFINITION( pInt, pInt );
PAIR_DEFINITION( float, int );

PAIR_PTR_DEFINITION( int, int );

MAKE_PAIR_DECLARE( int, int );
MAKE_PAIR_DECLARE( int, double );
MAKE_PAIR_DECLARE( double, double );
MAKE_PAIR_DECLARE( pInt, pInt );
MAKE_PAIR_DECLARE( float, int );

MAKE_PAIR_PTR_DECLARE( int, int );
PAIR_PTR_FREE_DECLARE( int, int );

#endif

そして、暗闇の中でそれらをバインドします (impl.c):

#include "defs.h"

MAKE_PAIR_DEFINE( int, int );
MAKE_PAIR_DEFINE( int, double );
MAKE_PAIR_DEFINE( double, double );
MAKE_PAIR_DEFINE( pInt, pInt );

// manual "instantiation"
MAKE_PAIR_SIGNATURE( float, int )
{
    PAIR_NAME( float, int ) local;

    local.first = V1;
    local.second = V2;
    return local;
}

MAKE_PAIR_PTR_DEFINE( int, int );
PAIR_PTR_FREE_DEFINE( int, int );

影が横たわるメインの土地で:

#include "defs.h"


int main(void)
{
    PAIR_NAME(int, int) myPairInts;
    PAIR_NAME( double, double) myPairDoubles;
    PAIR_NAME( pInt, pInt) myPairPointers;
    PAIR_NAME( float, int) myPairOther;

    PAIR_PTR_NAME( int, int ) pairPtr;


    myPairInts = PAIR_CREATE( int, int ) (1, 2);
    myPairDoubles = PAIR_CREATE( double, double ) (5, 6.5);
    myPairPointers = PAIR_CREATE( pInt, pInt) ( 0, 0 );
    myPairOther = PAIR_CREATE( float, int) (1, 1);

    pairPtr = PAIR_PTR_CREATE(int, int) (1, 2 );

    PAIR_PTR_FREE(int, int) (pairPtr );


    return 0;
}

PAIR_NAME値を含む構造体を作成し、値へのPAIR_PTR_NAMEポインターを含みます。PAIR_CREATE値をPAIR_PTR_CREATE作成し、ペア内にデータを入力します。

「impl.c」で必要なすべての組み合わせを定義する必要があります。どのコンパイル単位でも#include "defs.h"ペアを使用できます。

編集 - 質問への回答:

  1. 「これをライブラリなどで一度使用し、そのライブラリとペアの「テンプレート」の両方を使用するプログラムでもう一度使用すると、問題が発生しませんか?」

「pair.h」にはマクロのみが含まれており、どのライブラリやプログラムでも安全に使用できます。

重要なのは、同じ関数を 2 回定義しないことです。同じ構造を 2 回定義することもありません。

ただし、次のことを行うことができます: -上記のようにpair.h、defs.h、およびimpl.cを取得し、それらをライブラリに構築します(必要な__declspec(dllexport)およびを追加し__declspec(dllimportます)-その後#include pair.hdefs.hそこで定義されたペアをプログラムで使用できます- 新しいペアを使用したい場合は、これらの新しいペアを宣言および定義するためにnewと(float, float)new を追加する必要があるとします。ヘッダーは、ライブラリが提供するヘッダーと一緒に含めることができます。defs_my_program.himpl_my_program.cdefs_my_program.hdefs.h

ペアごとに 1 つの宣言と 1 つの定義を引き続き取得します。唯一の「欠点」は、ペアをオンザフライで実際に (安全に) 使用できないことです。それらは集中化する必要があります。

  1. 「型と関数名を選択した方法には、いくつかの問題があります。2 つの値を持つペアと 2 つのポインターを持つペアを分割します。また、1 つのポインターと 1 つの値を持つペアの特殊化も必要です。 1 つの値と 1 つのポインターを持つペアの場合。つまり、既に 4 つのケースのペアがあることになります。これをトリプレットまたはそれ以上のタプルに使用すると、2^n ケースを実装する必要があります。」

まず第一に、あなたはタプルを求めたのstd::pairではなく、求めました。

std::pairは と同等であることに注意してください。動的メモリを割り当てるPAIR_NAMEものはありません。std::pair

を自動的に使用する必要がない場合は、新しい専門化は必要ありませんmalloc。の例は、またはpIntの を作成できることを示しています。ポインターのが の外部から取得される必要があるだけです。pair(int, int*)(int*, int)pair

にメモリを自動的に割り当てるpairofが本当に必要な場合は、実際に必要な場合は自分で追加する必要があります。そうしないことを願っています。(int, int*)int*

pair値のタプルの場合、 apairと別の要素の aを使用するのは、あまり最適な解決策ではない可能性があります。これにより、3 つの要素を含む構造が得られます。そのようなことは、おそらくマクロマジックを介して行うことができ、心配している指数関数的な成長を削減できます.

于 2013-05-18T22:00:06.217 に答える
2

Declaring the struct multiple time is not a problem.

Defining the function multiple time is a problem. If you make the function static then it becomes a once per file issue. Even in C++ sometimes people will explicitly instantiate templates. You can have one file that has all the new functions.

If you want an automatic solution, then you need to look at how C++ compilers instantiate templates. Here is one method that was used ages ago.

  • Compile and link your code with no uses of PAIR_NEW
  • You will get undefined _new() functions.
  • Run a script that generates a C file with the proper PAIR_NEW() macro calls to define the undefined symbols
  • compile this new file and relink your project including the new file.
于 2013-05-18T21:12:40.847 に答える
1

それが良い考えであるかどうかの質問から抽象化すると、実装にいくつかの問題があります。

  • これは、これが;使用される場所での視覚的な期待に完全に反します。
  • C++ 以外では、パラメーターを受け取らない関数void().
  • アンダースコアと大文字で始まる名前は、すべてのコンテキストで C 実装用に予約されています。より良い命名規則を選択してください。
  • C++ 以外では、new関数はデータを初期化しません。これも不適切な命名規則です。
  • 識別子newをローカル変数として使用しています。いつかこれをC++にインターフェースしたい場合は悪いです
  • inline関数のマクロは、(1) 関数の名前をつなぐ命名規則、(2)定義、および (3) 関数シンボルを正確に生成できる外部「インスタンス」の3 つの部分に分割する必要があります。 1 つのコンパイル単位。
于 2013-05-18T21:16:49.753 に答える