Development Tip

C의 템플릿 시뮬레이션 (큐 데이터 유형용)

yourdevel 2020. 12. 12. 12:34
반응형

C의 템플릿 시뮬레이션 (큐 데이터 유형용)


queueC를 사용하여 구조 를 구현하려고합니다 . 제 구현은 매우 간단합니다. 큐는 ints 만 저장할 수 있으며 다른 것은 없습니다. 모든 데이터 유형을 보유 할 수 있도록 (아마 전처리기를 사용하여 ) C++템플릿을 시뮬레이션 할 수 있는지 궁금합니다 .C#definequeue

참고 : 를 사용하고 싶지 않습니다 void*. 약간 위험하고 기괴한 런타임 오류를 쉽게 일으킬 수 있다고 생각합니다.


그런 종류의 템플릿을 만들기 위해 미묘하고 추악한 트릭을 사용할 수 있습니다. 내가 할 일은 다음과 같습니다.

템플릿 목록 생성

목록을 정의하는 매크로

먼저 define_list(type)주어진 유형의 목록에 대한 모든 함수를 생성하는 매크로를 만듭니다. 그런 다음 모든 목록의 함수에 대한 함수 포인터를 포함하는 전역 구조를 만든 다음 목록의 각 인스턴스에 해당 전역 구조에 대한 포인터를 갖게됩니다 ( 가상 메서드 테이블 과 얼마나 유사한 지 확인 ). 이런 종류의 것 :

#define define_list(type) \
\
    struct _list_##type; \
    \
    typedef struct \
    { \
        int (*is_empty)(const struct _list_##type*); \
        size_t (*size)(const struct _list_##type*); \
        const type (*front)(const struct _list_##type*); \
        void (*push_front)(struct _list_##type*, type); \
    } _list_functions_##type; \
    \
    typedef struct _list_elem_##type \
    { \
        type _data; \
        struct _list_elem_##type* _next; \
    } list_elem_##type; \
    \
    typedef struct _list_##type \
    { \
        size_t _size; \
        list_elem_##type* _first; \
        list_elem_##type* _last; \
        _list_functions_##type* _functions; \
    } List_##type; \
    \
    List_##type* new_list_##type(); \
    bool list_is_empty_##type(const List_##type* list); \
    size_t list_size_##type(const List_##type* list); \
    const type list_front_##type(const List_##type* list); \
    void list_push_front_##type(List_##type* list, type elem); \
    \
    bool list_is_empty_##type(const List_##type* list) \
    { \
        return list->_size == 0; \
    } \
    \
    size_t list_size_##type(const List_##type* list) \
    { \
        return list->_size; \
    } \
    \
    const type list_front_##type(const List_##type* list) \
    { \
        return list->_first->_data; \
    } \
    \
    void list_push_front_##type(List_##type* list, type elem) \
    { \
        ... \
    } \
    \
    _list_functions_##type _list_funcs_##type = { \
        &list_is_empty_##type, \
        &list_size_##type, \
        &list_front_##type, \
        &list_push_front_##type, \
    }; \
    \
    List_##type* new_list_##type() \
    { \
        List_##type* res = (List_##type*) malloc(sizeof(List_##type)); \
        res->_size = 0; \
        res->_first = NULL; \
        res->_functions = &_list_funcs_##type; \
        return res; \
    }

#define List(type) \
    List_##type

#define new_list(type) \
    new_list_##type()

일반 인터페이스

저장된 함수 포인터를 통해 목록의 함수를 단순히 호출하는 몇 가지 매크로는 다음과 같습니다.

#define is_empty(collection) \
    collection->_functions->is_empty(collection)

#define size(collection) \
    collection->_functions->size(collection)

#define front(collection) \
    collection->_functions->front(collection)

#define push_front(collection, elem) \
    collection->_functions->push_front(collection, elem)

목록이 아닌 다른 컬렉션을 디자인하는 데 동일한 구조를 사용하면 좋은 포인터를 저장하는 모든 컬렉션에 대해 마지막 함수를 사용할 수 있습니다.

사용 예

마지막으로 새 목록 템플릿을 사용하는 방법에 대한 간단한 예입니다.

/* Define the data structures you need */
define_list(int)
define_list(float)

int main()
{
    List(int)* a = new_list(int);
    List(float)* b = new_list(float);

    push_front(a, 5);
    push_front(b, 5.2);
}

C에서 어떤 종류의 템플릿을 갖고 싶다면 그 정도의 트릭을 사용할 수 있지만, 그것은 다소 추합니다 (C ++를 사용하면 더 간단 할 것입니다). 유일한 오버 헤드는 데이터 구조의 인스턴스 당 포인터가 하나 더 많으므로 함수를 호출 할 때마다 한 번 더 간접적으로 발생합니다 (캐스트가 수행되지 않고 void*포인터 를 저장할 필요가 없습니다 . 예 \ o /). 당신이 그것을 사용하지 않기를 바랍니다 : p

한계

물론 실제 템플릿이 아닌 단순한 텍스트 대체 매크로를 사용하기 때문에 몇 가지 제한 사항이 있습니다.

한 번 정의

컴파일 단위당 한 번만 각 유형을 정의 할 수 있습니다. 그렇지 않으면 프로그램이 컴파일되지 않습니다. 예를 들어 라이브러리를 작성하고 일부 헤더에 몇 가지 define_지침이 포함 된 경우 이는 주요 단점이 될 수 있습니다 .

여러 단어 유형

생성하려는 경우 List그 템플릿 유형 여러 단어로 구성되어 ( signed char, unsigned long, const bar, struct foo...) 또는 그 템플릿 유형의 포인터 ( char*, void*...), 당신이해야 할 typedef첫 번째 유형.

define_list(int) /* OK */
define_list(char*) /* Error: pointer */
define_list(unsigned long) /* Error: several words */

typedef char* char_ptr;
typedef unsigned long ulong;
define_list(char_ptr) /* OK */
define_list(ulong) /* OK */

중첩 된 목록을 만들려면 동일한 트릭을 사용해야합니다.


글쎄, 내 마음에 떠오르는 유일한 가능성은 매크로 #define입니다. 아마도 다음과 같습니다.

queue.h :

#define TYPE int
#define TYPED_NAME(x) int_##x
#include "queue_impl.h"
#undef TYPE
#undef TYPED_NAME

#define TYPE float
#define TYPED_NAME(x) float_##x
#include "queue_impl.h"
#undef TYPE
#undef TYPED_NAME
...

queue_impl.h :

//no include guard, of course
typedef struct
{
    TYPE *data;
    ...
} TYPED_NAME(queue);

void TYPED_NAME(queue_insert) (TYPED_NAME(queue) *queue, TYPE data)
{
    ...
}

그것이 (내가하지 그런 처리기 전문가가되고 있는지의 100 % 야하는) 작동하는 경우, 그것은 당신에게 구조체를 제공해야 int_queue하고 float_queue기능과 함께,

void int_queue_insert(int_queue *queue, int data);
void float_queue_insert(float_queue *queue, float data);

물론 필요한 모든 유형에 대해 "템플릿"의 인스턴스화를 직접 수행해야하지만 이는에서 5 줄 블록을 반복하는 것과 같습니다 queue.h. 실제 구현은 한 번만 작성하면됩니다. 물론 이것을 더 다듬을 수 있지만 기본 아이디어는 명확해야합니다.

이것은 완벽하게 일치하는 인터페이스의 편리함이 부족하더라도 최소한 완벽하게 형식이 안전한 큐 템플릿을 제공합니다 (C는 오버로드 된 함수를 지원하지 않기 때문에 함수는 형식 이름을 전달해야합니다).


void * 데이터를 포함하는 큐를 구현하고이 void *를 모든 유형 또는 int와 같은 원시 유형에 대한 포인터로 해석합니다.

#define을 사용하는 것도 가능하지만 문제가있는 경우 디버깅에 대해 생각해보십시오.


다음은 (전처리기를 통해) 인스턴스화 하고 동일한 C 파일에서 여러 유형을 사용할 수있는 버전입니다 (주의, 토큰 연결 사용 ) .

#include <stdio.h>

#define DEFINE_LL_NODE(CONCRETE_TYPE) \
  struct node_of_ ## CONCRETE_TYPE \
    { \
      CONCRETE_TYPE data; \
      struct node_of_ ## CONCRETE_TYPE *next; \
    };

#define DECLARE_LL_NODE(CONCRETE_TYPE,VARIABLE_NAME) \
  struct node_of_ ## CONCRETE_TYPE VARIABLE_NAME;

/* Declarations for each type.  */
DEFINE_LL_NODE(int)
DEFINE_LL_NODE(char)

int main (void)
{
  /* Declaration of instances of each type.  */
  DECLARE_LL_NODE (int, foo)
  DECLARE_LL_NODE (char, bar)

  /* And you can then use these instances.  */
  foo.data = 1;
  foo.next = NULL;

  bar.data = 'c';
  bar.next = NULL;
}

로 전처리하면 다음과 같은 결과 cpp가 나타납니다.

struct node_of_int { int data; struct node_of_int *next; };

struct node_of_char { char data; struct node_of_char *next; };

int main (void)
{
  struct node_of_int foo;
  struct node_of_char bar;

  foo.data = 1;
  foo.next = ((void *)0);

  bar.data = 'c';
  bar.next = ((void *)0);
}

당신이 경우 정말 이 작업을 수행하려면, 그것은 간단한에 의해 해결 될 수있다 typedef:

typedef int data_t;

struct queue
{
  data_t* data;
}

이제 data_t일반 ints 대신 모든 장소에서 사용할 수 있습니다 . 그러나 한 번에 여러 유형을 사용할 수는 없습니다 (적어도 C ++ 템플릿의이 특정 동작을 일반 C에서 시뮬레이션 할 수있는 방법을 알 수 없습니다).


나는 이것에 대해 오랫동안 궁금해했지만 이제는 누구나 이해할 수있는 확실한 답을 얻었습니다. 그래서 보라!

데이터 구조 과정을 수강 할 때 데이터 구조, C 알고리즘에 대한 Standish의 책을 읽어야했습니다. 고통 스러웠다. 그것은 제네릭이 없었고, 불쌍한 표기법으로 가득 차 있었고, 거기에 있다는 보증이 없었던 전체적인 글로벌 상태 변이가있었습니다. 나는 그의 코드 스타일을 채택하는 것이 내 미래의 모든 프로젝트를 망쳐 놓는 것을 의미한다는 것을 알고 있었지만 더 나은 방법이 있다는 것을 알고 있었으므로 더 나은 방법이 있습니다.

이것은 내가 그것을 만지기 전의 모습입니다 (실제로 나는 그것을 인간이 읽을 수있는 방식으로 형식화하기 위해 어쨌든 만졌습니다, 당신은 환영합니다); 여러 수준에서 정말 추하고 틀렸지 만 참조 용으로 나열하겠습니다.

#include <stdio.h>

#define MaxIndex 100

int Find(int A[])
{
    int j;

    for (j = 0; j < MaxIndex; ++j) {
        if (A[j] < 0) {
            return j;
        }
    }

    return -1;
}

int main(void)
{
    // reminder: MaxIndex is 100.
    int A[MaxIndex];

    /**
     * anonymous scope #1
     *     initialize our array to [0..99],
     *     then set 18th element to its negative value(-18)
     *     to make the search more interesting.
     */
    {
        // loop index, nothing interesting here.
        int i;

        // initialize our array to [0..99].
        for (i = 0; i < MaxIndex; ++i) {
            A[i] = i * i;
        }

        A[17]= -A[17];
    }

    /**
     * anonymous scope #2
     *     find the index of the smallest number and print it.
     */
    {
        int result = Find(A);

        printf(
            "First negative integer in A found at index = %d.\n",
            result
        );
    }

    // wait for user input before closing.
    getchar();

    return 0;
}

이 프로그램은 끔찍하게 나쁜 스타일로 여러 가지 작업을 수행합니다. 특히 단일 범위 내에서만 사용되는 전역 매크로를 설정 한 다음 모든 코드를 계속 오염시킵니다. 매우 나쁘고 Windows API 규모의 글로벌 범위 오염을 대규모로 유발합니다.

또한이 프로그램은 인수를 포함 할 구조체없이 배열로 전달합니다. 즉, 배열이 Find 함수에 도달하면 도착시 종료됩니다. 우리는 더 이상 배열의 크기를 알지 못하므로 이제 main 및 Find가 전역 매크로에 종속되어 매우 나쁩니다.

이 문제를 없애고 코드를 단순하게 유지하는 두 가지 방법이 있습니다. 첫 번째 방법은 배열을 100 개의 정수 배열로 정의하는 전역 구조체를 만드는 것입니다. 이런 식으로 구조체를 전달하면 배열의 길이가 유지됩니다. 두 번째 방법은 배열의 길이를 find의 인수로 전달하고 배열을 만들기 전에 #define을 사용하고 범위가 여전히 sizeof를 통해 배열의 크기를 알기 때문에 바로 뒤에 #undef를 사용하는 것입니다. (A) / sizeof (A [0]) 런타임 오버 헤드가 0 인 경우 컴파일러는 100을 추론하여 붙여 넣습니다.

세 번째 방법으로이 문제를 해결하기 위해 일반적인 배열을 만드는 데 유용한 헤더를 만들었습니다. 추상적 인 데이터 유형이지만 자동화 된 데이터 구조라고 부르고 싶습니다.

SimpleArray.h

/**
 * Make sure that all the options needed are given in order to create our array.
 */
#ifdef OPTION_UNINSTALL
    #undef OPTION_ARRAY_TYPE
    #undef OPTION_ARRAY_LENGTH
    #undef OPTION_ARRAY_NAME    
#else 
    #if (!defined OPTION_ARRAY_TYPE) || !defined OPTION_ARRAY_LENGTH || (!defined OPTION_ARRAY_NAME)
        #error "type, length, and name must be known to create an Array."
    #endif

    /** 
     * Use the options to create a structure preserving structure for our array.
     *    that is, in contrast to pointers, raw arrays.
     */
    struct {
        OPTION_ARRAY_TYPE data[OPTION_ARRAY_LENGTH];
    } OPTION_ARRAY_NAME;

    /**
     * if we are asked to also zero out the memory, we do it.
     * if we are not granted access to string.h, brute force it.
     */
    #ifdef OPTION_ZERO_MEMORY
        #ifdef OPTION_GRANT_STRING
            memset(&OPTION_ARRAY_NAME, 0, OPTION_ARRAY_LENGTH * sizeof(OPTION_ARRAY_TYPE));
        #else
            /* anonymous scope */
            {
                int i;
                for (i = 0; i < OPTION_ARRAY_LENGTH; ++i) {
                    OPTION_ARRAY_NAME.data[i] = 0;
                }
            }
        #endif
        #undef OPTION_ZERO_MEMORY
    #endif
#endif

이 헤더는 기본적으로 C 전처리기를 사용해야하는 경우 모든 C 데이터 구조 헤더의 모습입니다 (PHP / Templating 툴킷 / ASP / 자신의 임베드 가능한 스크립팅 언어와는 대조적으로).

한 번 살펴 보겠습니다.

#include <stdio.h>

int Find(int A[], int A_length)
{
    int j;

    for (j = 0; j < A_length; ++j) {
        if (A[j] < 0) {
            return j;
        }
    }

    return -1;
}

int main(void)
{
    // std::array<int, 100> A;
    #define OPTION_ARRAY_TYPE int
    #define OPTION_ARRAY_LENGTH 100
    #define OPTION_ARRAY_NAME A
    #include "SimpleArray.h"    

    /**
     * anonymous scope #1
     *     initialize our array to [0..99],
     *     then set 18th element to its negative value(-18)
     *     to make the search more interesting.
     */
    {
        // loop index, nothing interesting here.
        int i;

        // initialize our array to [0..99].
        for (i = 0; i < (sizeof(A.data) / sizeof(A.data[0])); ++i) {
            A.data[i] = i * i;
        }

        A.data[17]= -A.data[17];
    }

    /**
     * anonymous scope #2
     *     find the index of the smallest number and print it.
     */
    {
        int result = Find(A.data, (sizeof(A.data) / sizeof(A.data[0])));

        printf(
            "First negative integer in A found at index = %d.\n",
            result
        );
    }

    // wait for user input before closing.
    getchar();

    // making sure all macros of SimpleArray do not affect any code
    // after this function; macros are file-wide, so we want to be 
    // respectful to our other functions.
    #define OPTION_UNINSTALL
    #include "SimpleArray.h"

    return 0;
}

참고, 우리는 순수한 C 및 C 전 처리기에서 순진한 std :: array를 발명했습니다! 우리는 매크로를 사용했지만 우리는 스스로 정리하기 때문에 악하지 않습니다! 우리의 모든 매크로는 범위의 끝에서 정의되지 않습니다.

문제가 있습니다. 우리가하지 않는 한, 우리는 더 이상 배열의 크기를 알 수 없습니다 (sizeof(A.data) / sizeof(A.data[0])). 이것은 컴파일러에 대한 오버 헤드가 없지만 어린이에게 친숙하지는 않습니다. 둘 다 매크로가 아니지만 여기서는 상자 내에서 작업하고 있습니다. 나중에 PHP와 같은 더 친숙한 전처리기를 사용하여 어린이에게 친숙하게 만들 수 있습니다.

이를 해결하기 위해 "자유로운"배열 데이터 구조에서 메소드 역할을하는 유틸리티 라이브러리를 만들 수 있습니다.

SimpleArrayUtils.h

/**
 * this is a smart collection that is created using options and is 
 *      removed from scope when included with uninstall option.
 *
 * there are no guards because this header is meant to be strategically
 *     installed and uninstalled, rather than kept at all times.
 */
#ifdef OPTION_UNINSTALL
    /* clean up */
    #undef ARRAY_FOREACH_BEGIN
    #undef ARRAY_FOREACH_END
    #undef ARRAY_LENGTH
#else
    /** 
     * array elements vary in number of bytes, encapsulate common use case 
     */
    #define ARRAY_LENGTH(A) \
        ((sizeof A.data) / (sizeof A.data[0]))

    /**
     * first half of a foreach loop, create an anonymous scope,
     * declare an iterator, and start accessing the items. 
     */
    #if defined OPTION_ARRAY_TYPE
        #define ARRAY_FOREACH_BEGIN(name, iter, arr)\
            {\
                unsigned int iter;\
                for (iter = 0; iter < ARRAY_LENGTH(arr); ++iter) {\
                    OPTION_ARRAY_TYPE name = arr.data[iter];
    #endif

    /** 
     * second half of a foreach loop, close the loop and the anonymous scope 
     */
    #define ARRAY_FOREACH_END \
            }\
        }
#endif

이것은 상당히 기능이 풍부한 라이브러리입니다.

ARRAY_LENGTH :: 데이터 필드가있는 모든 것-> int

OPTION_ARRAY_SIZE가 정의되어 있거나 재정의 된 경우 헤더는 foreach 루프를 수행하는 방법도 정의합니다. 귀여워요.

이제 미쳐 보자 :

SimpleArray.h

/**
 * Make sure that all the options needed are given in order to create our array.
 */
#ifdef OPTION_UNINSTALL
    #ifndef OPTION_ARRAY_TYPE
        #undef OPTION_ARRAY_TYPE
    #endif

    #ifndef OPTION_ARRAY_TYPE    
        #undef OPTION_ARRAY_LENGTH
    #endif

    #ifndef OPTION_ARRAY_NAME    
        #undef OPTION_ARRAY_NAME    
    #endif

    #ifndef OPTION_UNINSTALL
        #undef OPTION_UNINSTALL
    #endif
#else 
    #if (!defined OPTION_ARRAY_TYPE) || !defined OPTION_ARRAY_LENGTH || (!defined OPTION_ARRAY_NAME)
        #error "type, length, and name must be known to create an Array."
    #endif

    /** 
     * Use the options to create a structure preserving structure for our array.
     *    that is, in contrast to pointers, raw arrays.
     */
    struct {
        OPTION_ARRAY_TYPE data[OPTION_ARRAY_LENGTH];
    } OPTION_ARRAY_NAME;

    /**
     * if we are asked to also zero out the memory, we do it.
     * if we are not granted access to string.h, brute force it.
     */
    #ifdef OPTION_ZERO_MEMORY
        #ifdef OPTION_GRANT_STRING
            memset(&OPTION_ARRAY_NAME, 0, OPTION_ARRAY_LENGTH * sizeof(OPTION_ARRAY_TYPE));
        #else
            /* anonymous scope */
            {
                int i;
                for (i = 0; i < OPTION_ARRAY_LENGTH; ++i) {
                    OPTION_ARRAY_NAME.data[i] = 0;
                }
            }
        #endif
        #undef OPTION_ZERO_MEMORY
    #endif
#endif

SimpleArrayUtils.h

/**
 * this is a smart collection that is created using options and is 
 *      removed from scope when included with uninstall option.
 *
 * there are no guards because this header is meant to be strategically
 *     installed and uninstalled, rather than kept at all times.
 */
#ifdef OPTION_UNINSTALL
    /* clean up, be mindful of undef warnings if the macro is not defined. */
    #ifdef ARRAY_FOREACH_BEGIN
        #undef ARRAY_FOREACH_BEGIN
    #endif

    #ifdef ARRAY_FOREACH_END
        #undef ARRAY_FOREACH_END
    #endif

    #ifdef ARRAY_LENGTH
        #undef ARRAY_LENGTH
    #endif
#else
    /** 
     * array elements vary in number of bytes, encapsulate common use case 
     */
    #define ARRAY_LENGTH(A) \
        ((sizeof A.data) / (sizeof A.data[0]))

    /**
     * first half of a foreach loop, create an anonymous scope,
     * declare an iterator, and start accessing the items. 
     */
    #if defined OPTION_ARRAY_TYPE
        #define ARRAY_FOREACH_BEGIN(name, iter, arr)\
            {\
                unsigned int iter;\
                for (iter = 0; iter < ARRAY_LENGTH(arr); ++iter) {\
                    OPTION_ARRAY_TYPE name = arr.data[iter];
    #endif

    /** 
     * second half of a foreach loop, close the loop and the anonymous scope 
     */
    #define ARRAY_FOREACH_END \
            }\
        }
#endif

main.c

#include <stdio.h>

// std::array<int, 100> A;
#define OPTION_ARRAY_TYPE int
#define OPTION_ARRAY_LENGTH 100
#define OPTION_ARRAY_NAME A
#include "SimpleArray.h"  
#define OPTION_UNINSTALL
#include "SimpleArray.h"  

int Find(int A[], int A_length)
{
    int j;

    for (j = 0; j < A_length; ++j) {
        if (A[j] < 0) {
            return j;
        }
    }

    return -1;
}

int main(void)
{
    #define OPTION_ARRAY_NAME A
    #define OPTION_ARRAY_LENGTH (sizeof(A.data) / sizeof(A.data[0]))
    #define OPTION_ARRAY_TYPE int

    #include "SimpleArray.h"

    /**
     * anonymous scope #1
     *     initialize our array to [0..99],
     *     then set 18th element to its negative value(-18)
     *     to make the search more interesting.
     */
    {
        #include "SimpleArrayUtils.h"

        printf("size: %d.\n", ARRAY_LENGTH(A));

        ARRAY_FOREACH_BEGIN(item, i, A)
            A.data[i] = i * i;
        ARRAY_FOREACH_END

        A.data[17] = -A.data[17];


        // uninstall all macros.
        #define OPTION_UNINSTALL
        #include "SimpleArrayUtils.h"
    }

    /**
     * anonymous scope #2
     *     find the index of the smallest number and print it.
     */
    {
        #include "SimpleArrayUtils.h"        
        int result = Find(A.data, (sizeof(A.data) / sizeof(A.data[0])));

        printf(
            "First negative integer in A found at index = %d.\n",
            result
        );

        // uninstall all macros.
        #define OPTION_UNINSTALL
        #include "SimpleArrayUtils.h"
    }

    // wait for user input before closing.
    getchar();

    // making sure all macros of SimpleArray do not affect any code
    // after this function; macros are file-wide, so we want to be 
    // respectful to our other functions.
    #define OPTION_UNINSTALL
    #include "SimpleArray.h"

    return 0;
}

보시다시피, 이제 우리는 자유로운 추상화 (컴파일러가 우리를 대신 해줍니다)를 표현할 수 있고, 필요한만큼만 지불하고 (구조체) 나머지는 버려지고, 전역 범위를 오염시키지 않습니다.

여기서는 HTML 문서의 컨텍스트 밖에서 본 사람이 거의 없기 때문에 PHP의 힘을 강조합니다. 그러나 C 문서 또는 다른 텍스트 파일에서 사용할 수 있습니다. 템플릿 툴킷을 사용하여 원하는 스크립팅 언어를 매크로에 넣을 수 있습니다. 그리고 이러한 언어는 네임 스페이스, 변수 및 실제 함수가 있기 때문에 C 전 처리기보다 훨씬 낫습니다. 이렇게하면 코드를 생성하는 실제 스크립트를 디버깅하므로 디버깅이 더 쉬워집니다. 디버그하기에 지옥 인 C 전처리 기가 아니라, 주로 친숙 함 (올바른 마음으로 C 전처리기에 익숙해지기 위해 몇 시간을 소비 하는가?) 때문이다.

다음은 PHP로이 작업을 수행하는 예입니다.

SimpleArray.php

<?php
    class SimpleArray {
        public $length;
        public $name;
        public $type;

        function __construct($options) {
            $this->length = $options['length'];
            $this->name = $options['name'];
            $this->type = $options['type'];
        }

        function getArray() {
            echo ($this->name . '.data');
        }

        function __toString() {            
            return sprintf (
                "struct {\n" .
                "    %s data[%d];\n" .
                "} %s;\n"
                ,
                $this->type,
                $this->length,
                $this->name
            );          
        }
    };
?>

main.php

#include <stdio.h>
<?php include('SimpleArray.php'); ?>

int Find(int *A, int A_length)
{
    int i;

    for (i = 0; i < A_length; ++i) 
    {
        if (A[i] < 0) {
            return i;
        }
    }

    return -1;
}

int main(int argc, char **argv)
{
    <?php 
        $arr = new SimpleArray(array(
            'name' => 'A',
            'length' => 100,
            'type' => 'int'
        ));
        echo $arr;
    ?>

    printf("size of A: %d.\n", <?php echo($arr->length); ?>);

    /* anonymous scope */
    {
        int i;

        for (i = 0; i < <?php echo($arr->length)?>; ++i) {
            <?php $arr->getArray(); ?>[i] = i * i;
        }   
        <?php $arr->getArray(); ?>[17] = -<?php $arr->getArray()?>[17];
    }

    int result = Find(<?php $arr->getArray();?>, <?php echo $arr->length; ?>);
    printf(
        "First negative integer in A found at index = %d.\n",
        result
    );

    getchar();       

    return 0;
}

운영 php main.php > main.c

그때

gcc main.c -o main
./main

This looks a lot like Objective C, because this is essentially what objective C does, except it tends to link the "macros" of compile time to an actual runtime(as if php was available at runtime during the time C was running, and in turn your C can talk to php and php can talk to C, except the php is the smalltalkish language with many square brackets). The main difference is that Objective C does not to my knowledge have a way to make "static" constructs, as we did here; its objects are actually runtime, and as such are much more expensive to access, but are much more flexible, and preserve structure, whereas C structs collapse to bytes as soon as the header leaves the scope(whereas objects can be reflected back to their original state using internal tagged unions)...


You can't really get a high quality template work-alike in C with preprocessor macros; because, those macros expand only once, so at best you can get a data structure that can be retyped, but once processed is that type for the whole program.

That means you need to consider void * type solutions, which weaken C's type checking. To attempt to fix the weakened type checking, consider embedding a "type" field in your struct which is an "assign once at construction" string that represents the non void* type. Then you can possibly improve on the lack of type checking within the functions related to maintaining the struct. That is, if such a thing is even important to you.


Use one of the code gen macros in another answer and then finish it up with some C11 overload macros so you don't have to litter your call sites with too much type info.

http://en.cppreference.com/w/c/language/generic


#define q(t)                                    \
  typedef struct _q_##t {t v; struct q_##t *next} q_##t;

q(char);
q(int);

int              main(void)
{
  q_char         qc;
  q_int          qi;

  qc.v = 'c';
  qc.next = (void *) 0;

  qi.v = 42;
  qi.next = (void *) 0;

  return 0;
}

But I am not sure that's what you looking for...

참고URL : https://stackoverflow.com/questions/10950828/simulation-of-templates-in-c-for-a-queue-data-type

반응형