Подтвердить что ты не робот

Моделирование шаблонов в C (для типа данных очереди)

Я пытаюсь реализовать структуру queue, используя C. Моя реализация очень проста; очередь может содержать только int и больше ничего. Мне было интересно, смогу ли я имитировать шаблоны C++ в C (возможно, используя препроцессор #define), чтобы мой queue мог хранить любой тип данных.

Примечание. Я не хочу использовать void*. Я думаю, что это немного рискованно и может легко вызывать причудливые ошибки времени выполнения.

4b9b3361

Ответ 1

Вы можете использовать тонкие и уродливые трюки, чтобы создать такие шаблоны. Вот что я буду делать:

Создание шаблонного списка

Макроопределить список

Я бы сначала создал макрос - позвольте ему сказать 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, но это довольно уродливо (просто используйте С++, это будет проще). Единственные накладные расходы - это еще один указатель на экземпляр структуры данных и, таким образом, еще одно косвенное обращение, когда вы вызываете функцию (никакая броска не выполняется, вам не нужно хранить указатели 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 */

Вам нужно прибегнуть к тому же трюку, если вы хотите создать вложенные списки.

Ответ 2

Ну, единственная возможность, которая приходит мне на ум, - это макросы (#define s). Может быть, что-то вроде:

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 не поддерживает перегруженные функции).

Ответ 3

Реализовать очередь, содержащую данные void *, и интерпретировать этот void * как указатель на любой тип или даже примитивный тип типа int.

Использование #define возможно, но подумайте об отладке, если что-то не так...

Ответ 4

Вот версия, которая может позволить вам создать экземпляр (через препроцессор) и использовать несколько типов в одном файле 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);
}

Ответ 5

Если вы действительно этого хотите, его можно решить простым typedef:

typedef int data_t;

struct queue
{
  data_t* data;
}

Теперь вы можете использовать data_t во всех местах вместо простого int s. Обратите внимание, однако, что вы не сможете использовать сразу несколько типов (по крайней мере, я не вижу способа, как это конкретное поведение шаблонов С++ можно моделировать на простом C).

Ответ 6

Я давно задумывался об этом, но теперь у меня есть определенный ответ, который каждый может понять; так вот!

Когда я проходил курс Data Structures, мне пришлось прочитать книгу Standish о структурах данных, алгоритмах в C; было больно; у него не было никаких дженериков, оно было заполнено плохими обозначениями и целым кучей глобальных мутаций, в которых у него не было никаких ордеров; Я знал, что принятие его стиля кода означало навязывание всех моих будущих проектов, но я знал, что есть лучший способ, так вот, лучший способ:

Это то, на что это было похоже, прежде чем я коснулся его (на самом деле я все равно коснулся его, чтобы он был отформатирован так, как люди могут читать, пожалуйста); это действительно уродливо и неправильно на многих уровнях, но я перечислил его для справки:

#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; мы больше не знаем размер массива, поэтому теперь у нас есть основные и Find зависят от глобального макроса, очень плохо.

Есть два способа грубой силы, чтобы устранить эту проблему, но все же сохранить код простым; первый способ - создать глобальную структуру, которая определяет массив как массив из 100 целых чисел; этот способ передачи структуры сохранит длину массива в нем. Второй способ состоит в том, чтобы передать длину массива в качестве аргумента find и использовать только #define строку перед созданием массива, а #undef - сразу после того, как область будет по-прежнему знать размер массива через sizeof (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/собственного встраиваемого языка сценариев, будь то lisp).

Пусть возьмем его для спина:

#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;
}

BEHOLD, мы изобрели наивный std:: массив в чистом препроцессоре C и C! Мы использовали макросы, но мы не злые, потому что мы убираемся за собой! Все наши макросы undefd в конце нашей области.

Есть проблема; мы больше не знаем размер массива, если не делаем (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;
}

Как вы можете видеть; теперь у нас есть возможность выражать свободные абстракции (компилятор заменяет их на нас), мы платим только за то, что нам нужно (структуры), а остальные отбрасываются и не загрязняют глобальную область.

Я подчеркиваю силу PHP здесь, потому что немногие видели его вне контекста HTML-документов; но вы можете использовать его в документах C или любых других текстовых файлах. Вы можете использовать Templating Toolkit, чтобы вы могли использовать любой язык сценариев, который вам положен в макросы; и эти языки будут намного лучше, чем препроцессор C, потому что они имеют пространства имен, переменные и фактические функции; это облегчает их отладку, поскольку вы отлаживаете фактический script, который генерирует код; а не препроцессор 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

Это очень похоже на Objective C, потому что это, по сути, то, что делает цель C, за исключением того, что она связывает "макросы" времени компиляции с реальной средой выполнения (как если бы php был доступен во время выполнения во время выполнения C, и, в свою очередь, ваш C может разговаривать с php и php, может разговаривать с C, за исключением того, что php - это небольшой язык со многими квадратными скобками). Основное различие заключается в том, что Objective C, насколько мне известно, не имеет возможности создавать "статические" конструкции, как мы это делали здесь; его объекты на самом деле являются исполняемыми и, как таковые, намного более дорогими для доступа, но гораздо более гибкими и сохраняют структуру, тогда как структуры C сворачиваются в байты, как только заголовок покидает область действия (тогда как объекты могут быть возвращены в исходное с использованием внутренних меченых объединений)...

Ответ 7

Вы не можете получить шаблон высокого качества, как в C, с макросами препроцессора; потому что эти макросы расширяются только один раз, поэтому в лучшем случае вы можете получить структуру данных, которую можно перепечатать, но как только обработанный тип этого типа для всей программы.

Это означает, что вам нужно рассмотреть решения типа void *, которые ослабляют проверку типа C. Чтобы попытаться исправить ослабленную проверку типа, рассмотрите вопрос о вложении поля типа в вашу структуру, которая является строкой "присваивать один раз при построении", которая представляет тип non void *. Тогда вы можете улучшить отсутствие проверки типов внутри функций, связанных с поддержкой структуры. То есть, если такая вещь даже важна для вас.

Ответ 8

Используйте один из макросов кода gen в другом ответе, а затем завершите его с помощью некоторых макросов перегрузки C11, поэтому вам не нужно помешать своим сайтам со слишком большой информацией о типе.

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

Ответ 9

#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;
}

Но я не уверен, что вы ищете...