void ** параметр без временной переменной

У меня есть функция с таким прототипом:

ErrorType function(void ** parameter, other_args);

Эта функция считывает указатель, на который указывает «параметр», и изменяет его (подумайте об этом как о realloc).

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

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

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

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

Итак, вопрос в том, есть ли способ сделать это без особых проблем в компиляторе C11?


person Mabus    schedule 19.08.2014    source источник
comment
Невозможно найти способ без постоянного хранилища для возвращаемого типа. Если у нас есть место для этого, это легко.   -  person Deduplicator    schedule 19.08.2014
comment
Я также думал о TLS, и это может быть «хак» для этого. Но, насколько я знаю, у TLS есть две проблемы: у него может быть ограниченный ресурс, и у него могут быть проблемы с повторным входом при наличии сигналов.   -  person Mabus    schedule 19.08.2014
comment
Как вы думаете, почему стандарт C требует такого приведения? Я считаю, что броски в пустоту и обратно в порядке. Вы получаете какое-то предупреждение? Поскольку многие библиотеки делают это везде и не вызывают проблем, именно для этой цели и существует пустота...   -  person Vality    schedule 19.08.2014
comment
@Vality: например. приведение double ** к void ** допустимо, переход к function тоже допустим, но разыменование является нарушением строгого алиасинга (доступ к объекту double * с lvalue типа void *). Кроме того, могут быть проблемы с разными представлениями для void * и double * (но я считаю, что это не проблема на распространенных платформах).   -  person mafso    schedule 19.08.2014
comment
Преобразования между любым типом указателя и void* допустимы, и приведение типов не требуется (в C). Но здесь есть пустота**, так что вы не можете назначить дубль** (даже с приведением, я думаю, по крайней мере, непереносимо).   -  person Mabus    schedule 19.08.2014
comment
@mafso Однако обычно нужно было бы приводить к char **. Стандарт гарантирует, что char * может безопасно использовать псевдоним любого другого указателя.   -  person Vality    schedule 19.08.2014
comment
@Vality: вы можете получить доступ ко всему через lvalue символьного типа, да, но не через один из типа указатель на символ.   -  person mafso    schedule 19.08.2014
comment
@mafso Хмн ... Глядя на это, я вижу один безопасный способ сделать это: memcpy ваш указатель поверх другого, memcpy гарантированно безопасен в отношении псевдонимов, несмотря на приведение   -  person Vality    schedule 19.08.2014
comment
@Vality Вы предполагаете, что все указатели имеют одинаковый размер, но в стандарте C этого не сказано.   -  person Mabus    schedule 19.08.2014
comment
@Vality В дополнение к размерам, требованиям к представлению и выравниванию, объекты memcpyed помнят свой исходный тип (эффективный тип в стандарте), поэтому строгое присвоение псевдонимов по-прежнему является проблемой. Я не думаю, что это обсуждение приведет к ответу на вопрос здесь, поэтому, если вам интересно, давайте продолжим в чате.   -  person mafso    schedule 19.08.2014
comment
Функциональный подход обсуждается, например. здесь (с выводом, что нет строго соответствующего способа), макрос-подход будет работать, если вы передадите переменную ошибки (которая также могут быть возвращены), статические переменные/переменные TLS (как обсуждалось выше) или как сказано в вопросе с выражениями. Я не знаю другого пути.   -  person mafso    schedule 19.08.2014


Ответы (1)


Если я правильно понимаю вопрос, вы хотели бы, чтобы function мог изменять различные типы указателей. Ну, есть плохие новости и хорошие новости об этом.

Плохая новость: объектное представление указателей непрозрачно, поэтому вам нужно сообщить вашему function, с каким типом указателя он должен работать, если только ваша функция не гарантирует копирование объектного представления из исходного представления указателя, и вы знаете что оба представления имеют одно и то же значение.

Например, sizeof (double *) может отличаться от sizeof (void *).

Хорошие новости: указатель на любой тип объекта может быть приведен к void * и обратно, включая void ** и double ** любые другие типы указателей. Итак, вы могли бы:

ErrorType function(void * ptr, int ptr_type, ...) {
    void ** vpp;
    double ** dpp;
    ...

    ...
    switch (ptr_type) {
        case PTR_TYPE_VOIDP:
        vpp = ptr;
        /* Now you can work with *vpp */
        *vpp = ...
        break;

        case PTR_TYPE_DOUBLEP:
        dpp = ptr;
        /* Now you can work with *dpp */
        *dpp = ...
        break;
      }
    ...
  }
person Shao    schedule 29.08.2014
comment
Но это много работы. Мне пришлось бы делать разный код для каждого типа, а в C очень много разных типов. - person Mabus; 29.08.2014
comment
Согласованный. В некоторых системах указатель есть указатель, и все они одинаковы. Но C не поощряет это, и для переносимого кода придется иметь дело с каждым типом указателя. char *, unsigned char *, signed char * и void * имеют одинаковое представление и выравнивание, поэтому вы можете обрабатывать их все вместе. Любой struct foo * будет таким же, как и любой другой struct bar *. Любой union foo * будет таким же, как и любой другой union bar *. Но остальные не уточняются. - person Shao; 30.08.2014