0

-edit- I narrowed it down. Reproducible: Why does passing this object in C break my code?

My app is not working properly after i made a change. I got a warning in msvc but not in gcc. Heres a repo of the warning.

warning C4190: 'fnA' has C-linkage specified, but returns UDT 'Test' which is incompatible with 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);
}

AFAIK there should be no reason why i can't use Test as a return value; This may not be the problem but this may be the problem. I can't figure out the problem but i get runtime oddities that i can not reproduce (both gcc and msvc). I suspected the problem would be MyString is corrupted but it appears that isnt the case which made me quite confused. Heres my wrapper. MyString is >8bytes and i need to hand this off to C code which returns everything by int unfortunately. Which is why i put the static assert to see if the class size is what i expected. Now that size/splicing is handled, i'm still completely like WTF!?! Why does that warning exist and is there anything i can possibly do to fix it?

Even writing class Test{ T t; }; causes the warning however struct fixes it. struct with private breaks it and i need t to be private.

OK! After I removed the constructors in WrappedPointer and changed the class to struct (which makes lhs public). It runs perfectly in GCC and MSVC. Changing struct WrappedPointer to class WrappedPointer breaks my code. WTF!?! This is a debug build too, not optimized. Why on earth does changing the keyword struct to class break the code!?! WTF!?! BUT that change doesnt break gcc. Using non default constructors break 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*),"");
Community
  • 1
  • 1
  • What is the C code that calls `fnA` doing with the object it receives? Since there are no classes in C, you should write a function that does not return a class and use that from within the C code. – jogojapan Mar 09 '12 at 04:04
  • @jogojapan: fnA is just an example to cause the error. What do you mean not return a class !?! are you saying stick it in a global variable!?! useless as well since the code may call this function multiple times before calling my code. –  Mar 09 '12 at 04:20
  • What I mean is this: It is alright to use C++ within `external "C"`, but if the C++ types you use there end up being **returned to C code**, what should the C code do about them? It can't deal with a class. – jogojapan Mar 09 '12 at 04:22
  • According to [here](http://msdn.microsoft.com/ja-jp/library/cc367895.aspx) the code is legal if the definition of the function is in C++ which it is not. – Jesse Good Mar 09 '12 at 05:11
  • @Jesse There is a second condition there: "All calls to this function occur from C++." It is my understanding that _both_ conditions must be met. Also, the _definition_ of the function _is_ in C++, isn't it. – jogojapan Mar 09 '12 at 05:16
  • @Jesse: Actually it is defined in C++. If you say it isn't bc extern "C" is there than by your logic it is impossible to define the function in C++. Anyways its the other clause, calling the function from C++ which isnt true. Also it doesnt explain why GCC cant handle constructors (but can handle it if theres no constructors) and give no warnings about it –  Mar 09 '12 at 05:20

3 Answers3

2

The external "C" marks the function to have C linkage and disables name mangling. Now the issue in your function is that the argument is a template (the typedef only creates an alias in the current translation unit, A is still Test<int> for all purposes), and that name must be mangled.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Out of all the answers this is the only one that was a shred of help. There is no error however. I'd like the struct which is meant to be used everywhere to have operator overloading (for vector::find) and the occasional implicit conversion. Is there anything i can do to have my own type? Making it a POD seems to do the trick however like i said if i make lhs private it crashes which is extremely weird. I don't do any POD checks anywhere, making one member private shouldnt break code yet it does (actually it doesnt for GCC but gcc breaks when i use a non default constructor) –  Mar 09 '12 at 04:48
  • Why do you want the function to be `extern "C"`? That need will probably drive the options that are available. – David Rodríguez - dribeas Mar 09 '12 at 12:54
0
extern "C" {
    A fnA(A v) { return v; }
}

This says that fnA is a C function. C does not have object-oriented programming, or templates. The function uses both. Therefore, it cannot link as a C function.

chris
  • 60,560
  • 13
  • 143
  • 205
  • 2
    Strictly speaking, `extern "C"` means that the name mangling used in the object file will be compatible with C. You can use C++ inside the function definition. But if the _function prototype_ (i.e. the definition of return type and argument types) involve C++ data types, you will have a problem, in particular when actual C code calls the function. – jogojapan Mar 09 '12 at 04:12
  • 1
    Thanks for the heads-up. I was thinking it should be fully compatible with C, but I guess it's only in the interfaced areas. – chris Mar 09 '12 at 05:02
0
  • Classes are not valid in C.
  • Templates are not valid in C.
  • Templated classes are certainly not valid in C.

Pick your language and stick to it!

John3136
  • 28,809
  • 4
  • 51
  • 69
  • You make it sound as if there was never a good reason to combine C++ code and C code. I believe there _can_ be good reasons. And sometimes people must do it even if the reasons aren't very good. – jogojapan Mar 09 '12 at 04:30
  • Combining C and C++ is fine for calling a C library from C++ or similar, but what you the OP is trying to do makes no sense - what is he expecting to do with a templated class in C? As @Chris said below, the best you could really do is cast it to a void* and then somewehere else back to a real type - but if you have to do that I'd question the design! – John3136 Mar 09 '12 at 04:34
  • I'm using a C library which demand me to return ints and voids* in all my functions simply because thats all C knows. –  Mar 09 '12 at 04:43