2009-08-02 3 views
4

Недавно я обнаружил, что что-то компилируется (не уверен, что это законно). Моя потребность в таких вещах исходит из этого: Мой проект выводит машинный код для выбранной арки (который может быть или не быть тем же самым арку, что и тот, который запускает программу). Итак, я хотел бы поддерживать до 64-битных архитектур прямо сейчас (в то же время поддерживая существующие 32 и 16-битные дуги.) Мое текущее решение для базы base_state - просто быть uint64_t и вручную отбрасывать до 16 и 32 бит по мере необходимости , Хотя, я обнаружил, что вы можете скомпилировать объединения в параметре функции. Таким образом, функция по мере компиляции:с использованием объединений в функциональных параметрах

int pcg_new_state(pcg_state *s,int arch,void *mem,int sz, 
      union{ 
    uint16_t b16; 
    uint32_t b32; 
    uint64_t b64; 
}base ,int self_running); 

Действительно ли такая вещь вообще «законна» или поддерживается другими компиляторами? Кроме того, я не могу понять, как вызвать эту функцию, не создавая союз, а затем передавая этот союз new_state.

+0

Я не thinka союз подсчитываются как тип в этой ситуации, хотя. но это он: [earlz @ EarlzBeta- ~] $ gcc -v! Чтение спецификаций из /usr/lib/gcc-lib/amd64-unknown-openbsd4.5/3.3.5/specs! Конфигурируется с помощью:! Модель ничьи: single! gcc версия 3.3.5 (прополис)! (! Означает новую строку, так как комментарии не получают форматирование.) – Earlz

+2

Почему бы вам просто не объявить союз как обычно, с именем? – 2009-08-02 22:14:01

+0

Я полагаю, что мог бы, хотя я хочу, чтобы иметь возможность вызвать эту функцию без объявления новой переменной union и т. Д., Возможно ли это? – Earlz

ответ

6

Резюмируя: Да, это v alid в C, хотя он является незаконным в C++. Последний содержит эту записку, в которой объясняет разницу

Change: In C++, types may not be defined in return or parameter types. In C, these type definitions are allowed

Example:

void f(struct S { int a; } arg) {} // valid C, invalid C++ 
enum E { A, B, C } f() {} // valid C, invalid C++ 
  • Обоснование: При сравнении типов в различных единицах компиляции, C++ опирается на имя эквивалентности, когда C зависит от структурной эквивалентности. Что касается типов параметров: поскольку тип, определенный в списке параметров, будет находиться в области функции, единственные юридические вызовы в C++ будут изнутри самой функции.
  • Влияние на оригинальную функцию: Исключение семантически четко определенной функции.
  • Сложность конвертации: Семантическая трансформация. Определения типов должны быть перемещены в область файла или в заголовочные файлы.
  • Насколько широко используется: Редко. Этот стиль определений типов рассматривается как плохой стиль кодирования.

Структурная эквивалентность в C выполняется по понятию «совместимость типов». Это позволяет C обрабатывать многие типы, как если бы они были идентичны, хотя они теоретически различны - потому что они объявлены в двух разных единицах перевода. В C++ эта концепция не существует, поскольку типы имеют привязку и сопоставляются с одним и тем же объектом (например, чтобы функции-члены могли связываться друг с другом).

Обратите внимание, что приведенное выше объяснение основано на C89, которое не учитывало имя тега структуры при определении совместимости типов. В C89 draft, соответствующий текст гласит следующее:

Moreover, two structure, union, or enumeration types declared in separate translation units are compatible if they have the same number of members, the same member names, and compatible member types; for two structures, the members shall be in the same order;

В C99, проверка типа более строже: Если одна структуры имеет имя тега, другая декларация структуры должна иметь то же имя тега. Поэтому в вашем случае типа unnamed union, чтобы объявить функцию в другом TU, имеющем совместимый тип, вам понадобится unnamed union снова, если вы хотите иметь действительный код C99 (без неопределенного поведения) - вы не можете «обмануть», и использовать названный союз в одном TU и неназванный союз в другом TU. Мне кажется, что этот «трюк» действителен для C89. C99 TC3 6.2.7/1:

Moreover, two structure, union, or enumerated types declared in separate translation units are compatible if their tags and members satisfy the following requirements: If one is declared with a tag, the other shall be declared with the same tag. If both are complete types, then the following additional requirements apply: there shall be a one-to-one correspondence between their members such that each pair of corresponding members are declared with compatible types, and such that if one member of a corresponding pair is declared with a name, the other member is declared with the same name. For two structures, corresponding members shall be declared in the same order.


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

Итак, для этого вам придется иметь аргумент, совместимый с типом параметра. Для двух объединений, объявленных в , такой же блок перевода, это означает, что их тип должен быть равен - это единственный способ создать совместимый тип в пределах одной единицы перевода. Но это не сработает, потому что объявление неназванного объединения создает уникальный новый тип - никак не «ссылаться» на него с использованием другого объявления.

Итак, подведем итоги - вы должны указать название союза. Чтобы не создавать отдельную переменную для передачи необходимой базы аргумент, я бы объявить его вне функции, а также создавать функции, которые дают обратно в союз, вы можете пройти через

union base_type { 
     uint16_t b16; 
     uint32_t b32; 
     uint64_t b64; 
}; 

int pcg_new_state(pcg_state *s,int arch,void *mem,int sz, 
        union base_type base,int self_running); 

union base_type base_b16(uint16_t t) 
{ union base_type b; b.b16 = t; return b; } 
union base_type base_b32(uint32_t t) 
{ union base_type b; b.b32 = t; return b; } 
union base_type base_b64(uint64_t t) 
{ union base_type b; b.b64 = t; return b; } 

Теперь он может выглядеть следующим образом

pcg_new_state(...., base_b32(4211), ....); 
+0

+1 Но вы создаете именованный союзный экземпляр, хотя и в функциях. Мое понимание (что вполне может быть ошибочным) состояло в том, что OP не хотел создавать экземпляр нигде, кроме списка параметров (где он был бы безымянным). И я не думаю, что это возможно. – 2009-08-02 23:21:28

+0

Да, я хотел предложить «альтернативу» своему пути :) Я думал, что главной причиной для него было не нравиться это шаблонный код для создания локальной переменной. С помощью функции обертки больше не нужно: –

+0

да, это приемлемая альтернатива для меня .. Я просто не хочу прибегать к массовым количествам (uint32_t) или временным переменным, а также для вызов, который будет удобен для пользователя функции. И никакая производительность, вызванная функциональными вызовами, не приводит к тому, что они достаточно просты, чтобы быть встроенными. – Earlz

1

Я быстро просмотрел стандарт (Edit: C++) и не видел ничего запрещающего в 8.3.5 - Functions [dcl.fct] или 9.5 - Unions [class.union]. Как полуобразованное предположение, я бы сказал, что законно передавать союз, но не объявлять такой встроенный. GCC дает:

Ошибка: типы не могут быть определены в типах параметров.

Таким образом, вам нужно будет определить тип раньше времени.

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

+0

перегрузка невозможна с такими целыми типами, и я использую C не C++, поэтому это все равно выкидывает окно. – Earlz

+1

Я думаю, что это совершенно верно C. Но я думаю, что вы не можете передать аргумент (как бы вы создали объединение правильного типа?) - если вы не передадите нулевой указатель, а параметр - указатель. –

+0

Думая об этом, это выглядит как действительный код C для меня: * TU 1 *: 'void f (union A {int a; char b;} c) {...}' и в * TU 2 *: 'union A {int a; char b; }; void f (объединение A c); int main (void) {объединение A a = {1}; е (а); } '- оба союза имеют совместимые типы. –

0

Как об этом:

typedef struct _base 
{ 
    union 
    { 
     uint16_t b16; 
     uint32_t b32; 
     uint64_t b64; 
    }; 
}Base; 

int func(Base b) 
{ 
    return 0; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 

    Base b; 
    b.b16 = 0xFFFF; 
    func(b); 

    return 0; 

} 
0

Вы можете получить это собрать в GCC, обеспечивая союз с именем в функции, но вы не сможете его использовать из-за сферу действия Defintion как GCC предупреждает:

test_union.c:14: warning: ‘union X’ declared inside parameter list 
test_union.c:14: warning: its scope is only this definition or declaration, which is probably not what you want 

Код:

int pcg_new_state(pcg_state *s,int arch,void *mem,int sz, 
         union X{ 
     uint16_t b16; 
     uint32_t b32; 
     uint64_t b64; 
}base ,int self_running); 
0

Размер союза будет размером с наибольший член в любом случае - так что вы ничего не получите. Вы могли бы просто сделать параметр uint64_t, поскольку преобразования из более крупных и меньших неподписанных типов хорошо определены и обычно дешевы для реализации. (Например, присвоение uint64_t uint16_t может быть выполнено только с помощью младших 16 бит более широкого типа).

EDIT: Например

int pcg_new_state(pcg_state *s,int arch,void *mem,int sz, uint64_t param_base ,int self_running) 
{ 
    /* Implementation for 16 bit arch */ 
    uint16_t base = param_base; 

    /* ... more code ... */ 
} 
+0

есть. но тогда все должно быть заброшено в uint ** _ t или сделана временная переменная. Оба из них являются fugly, если у вас очень много кода в функции. И вам не кажется, что стоит сказать, что 16-битная арка постоянно бросает 64-битный int на 16-битный int? (Возьмите 8086, он едва поддерживает 32-разрядные преобразования чисел в наборе опкодов) – Earlz

+0

Вытягивание uint16_t из union, который включает uint64_t, больше не работает для процессора, чем преобразование uint64_t в uint16_t - вот моя точка. – caf

Смежные вопросы