Мой код может дать сбой, потому что «класс» не совместим с C?

-edit- Я сузил его. Воспроизводимый: почему передача этого объекта в C прерывается мой код?

Мое приложение не работает должным образом после того, как я внес изменения. Я получил предупреждение в msvc, но не в gcc. Вот репо предупреждения.

предупреждение C4190: «fnA» имеет указанную C-связь, но возвращает UDT «Test», который несовместим с C

#include <type_traits> 

template<class T>
class Test{
        T t;
};
typedef Test<int> A;
//static_assert(std::is_pod<A>::value, "Not a POD"); //fails in msvc 2010
static_assert(sizeof(A) == sizeof(int), "");
static_assert(sizeof(A) == sizeof(void*), "");
extern "C" {
        A fnA(A v) { return v; }
}
int main() {
        A a;
        fnA(a);
}

Насколько я знаю, не должно быть причин, по которым я не могу использовать Test в качестве возвращаемого значения; Это может быть не проблема, но это может быть проблемой. Я не могу понять проблему, но у меня возникают странности во время выполнения, которые я не могу воспроизвести (как gcc, так и msvc). Я подозревал, что проблема в том, что MyString повреждена, но, похоже, это не тот случай, который меня сильно смутил. Вот моя обертка. MyString составляет> 8 байт, и мне нужно передать это коду C, который, к сожалению, возвращает все через int. Вот почему я поставил статическое утверждение, чтобы увидеть, соответствует ли размер класса тому, что я ожидал. Теперь, когда размер/склейка обработаны, я все еще полностью как WTF!?! Почему это предупреждение существует и есть ли что-нибудь, что я могу сделать, чтобы исправить это?

Даже запись class Test{ T t; }; вызывает предупреждение, однако struct исправляет его. структура с приватными разрывами, и мне не нужно быть приватными.

ХОРОШО! После того, как я удалил конструкторы в WrappedPointer и изменил класс на struct (что делает lhs общедоступным). Он отлично работает в GCC и MSVC. Изменение struct WrappedPointer на class WrappedPointer ломает мой код. ВТФ!?! Это тоже отладочная сборка, не оптимизированная. С какой стати изменение ключевого слова struct на class нарушает код!?! ВТФ!?! НО это изменение не нарушает gcc. Использование конструкторов не по умолчанию ломает gcc...

template <class T>
struct WrappedPointer {
//private:
    T* lhs;
public:
    void SetLHS(T*v) { lhs=v; }
    //WrappedPointer(){}
    //WrappedPointer(T*value) : lhs(value){}
    //WrappedPointer(const WrappedPointer&v) : lhs(v.lhs){}
    T* operator->() const { return lhs; }
    T* operator*() const { return lhs; }
    template<class TT>
    bool operator==(TT t) const { return *lhs==t; }
    template<class TT>
    bool operator!=(TT t) const { return *lhs!=t; }

    bool operator==(int v) const { myassert2(v==0); return lhs==0; }
    bool operator!=(int v) const { myassert2(v==0); return lhs!=0; }
    bool operator==(const WrappedPointer&t) const { return *lhs==*t; }
    bool operator!=(const WrappedPointer&t) const { return *lhs!=*t; }
}
typedef WrappedPointer<MyString> String;
//typedef MyString* String;
static_assert(sizeof(String) == sizeof(int), "");
static_assert(sizeof(String) == sizeof(void*),"");

person Community    schedule 09.03.2012    source источник
comment
Что делает код C, вызывающий fnA, с полученным объектом? Поскольку в C нет классов, вы должны написать функцию, которая не возвращает класс, и использовать ее внутри кода C.   -  person jogojapan    schedule 09.03.2012
comment
@jogojapan: fnA - это просто пример, вызывающий ошибку. Что значит не вернуть класс!?! вы говорите вставьте это в глобальную переменную!?! также бесполезно, поскольку код может вызывать эту функцию несколько раз перед вызовом моего кода.   -  person    schedule 09.03.2012
comment
Я имею в виду следующее: можно использовать C++ в external "C", но если типы C++, которые вы там используете, в конечном итоге возвращаются в код C, что код C должен с ними делать? Он не может иметь дело с классом.   -  person jogojapan    schedule 09.03.2012
comment
Согласно здесь код допустим, если определение функции в С++, которого нет.   -  person Jesse Good    schedule 09.03.2012
comment
@Jesse Там есть второе условие: все вызовы этой функции происходят из C++. Насколько я понимаю, должны быть выполнены оба условия. Кроме того, определение функции есть в C++, не так ли?   -  person jogojapan    schedule 09.03.2012
comment
@Jesse: На самом деле это определено в C++. Если вы говорите, что это не bc extern C, то по вашей логике невозможно определить функцию на C++. В любом случае это другое предложение, вызывающее функцию из C++, что неверно. Также это не объясняет, почему GCC не может обрабатывать конструкторы (но может обрабатывать, если конструкторов нет) и не выдает предупреждений об этом.   -  person    schedule 09.03.2012


Ответы (3)


external "C" помечает функцию как связанную с C, и отключает изменение имени. Теперь проблема в вашей функции заключается в том, что аргумент является шаблоном (typedef создает псевдоним только в текущей единице перевода, A по-прежнему является Test<int> для всех целей), и это имя должно< /em> быть искалеченным.

person David Rodríguez - dribeas    schedule 09.03.2012
comment
Из всех ответов это единственный, который хоть немного помог. Однако ошибки нет. Я бы хотел, чтобы структура, предназначенная для повсеместного использования, имела перегрузку операторов (для vector::find) и случайные неявные преобразования. Есть ли что-нибудь, что я могу сделать, чтобы иметь свой собственный тип? Создание POD, кажется, помогает, однако, как я уже сказал, если я сделаю lhs private, он выйдет из строя, что очень странно. Я нигде не проверяю POD, делая один член закрытым, он не должен ломать код, но он это делает (на самом деле это не для GCC, но gcc ломается, когда я использую конструктор не по умолчанию) - person ; 09.03.2012
comment
Почему вы хотите, чтобы функция была extern "C"? Эта потребность, вероятно, будет определять доступные варианты. - person David Rodríguez - dribeas; 09.03.2012

extern "C" {
    A fnA(A v) { return v; }
}

Это говорит о том, что fnA является функцией C. C не имеет объектно-ориентированного программирования или шаблонов. Функция использует оба. Следовательно, он не может быть связан как функция C.

person chris    schedule 09.03.2012
comment
Строго говоря, extern "C" означает, что изменение имени, используемое в объектном файле, будет совместимо с C. Вы можете использовать C++ внутри определения функции. Но если прототип функции (т. е. определение типа возвращаемого значения и типов аргументов) включает типы данных C++, у вас возникнут проблемы, в частности, когда реальный код C вызывает функцию. - person jogojapan; 09.03.2012
comment
Спасибо за предупреждение. Я думал, что он должен быть полностью совместим с C, но я думаю, что это только в интерфейсных областях. - person chris; 09.03.2012

  • Классы недействительны в C.
  • Шаблоны недействительны в C.
  • Шаблонные классы, безусловно, недействительны в C.

Выберите свой язык и придерживайтесь его!

person John3136    schedule 09.03.2012
comment
Вы говорите так, как будто никогда не было веских причин для объединения кода C++ и кода C. Я считаю, что могут быть веские причины. И иногда люди должны это делать, даже если причины не очень веские. - person jogojapan; 09.03.2012
comment
Объединение C и C++ подходит для вызова библиотеки C из C++ или аналогичного, но то, что вы пытаетесь сделать, не имеет смысла - что он ожидает делать с шаблонным классом в C? Как сказал @Chris ниже, лучшее, что вы могли бы сделать, это бросить его в пустоту *, а затем где-то еще вернуться к реальному типу, но если вам нужно это сделать, я бы поставил под сомнение дизайн! - person John3136; 09.03.2012
comment
Я использую библиотеку C, которая требует от меня возвращать целые числа и пустоты * во всех моих функциях просто потому, что это все, что знает C. - person ; 09.03.2012