2013-05-17 10 views
21

Po prostu przyszło mi do głowy, że standard C++ mówi, że funkcje C i C++ mają różne i niekompatybilne typy, nawet jeśli ich sygnatury są takie same (więcej informacji można znaleźć w artykule this question). Oznacza to, że technicznie nie można przekazać funkcji C++ do funkcji C, takiej jak pthread_create().Jakie platformy mają niekompatybilne ABI dla C i C++?

Jestem ciekawy, czy są jakieś platformy w użyciu, gdzie dwa ABI są w rzeczywistości różne (poza oczywistymi różnicami w mangowaniu nazwy). Mówiąc konkretnie, czy ktoś wie o platformach, na których programowi C++ nie uda się skompilować i uruchomić?

#include <assert.h> 

extern "C" int run(int (*f)(int), int x) { return f(x); } 

int times2(int x) { return x * 2; } 

int main(int argc, char *argv[]) { 
    int a = times2(argc); 
    // This is undefined behavior according to C++ because I am passing an 
    // "extern C++" function pointer to an "extern C" function. 
    int b = run(&times2, argc); 
    assert(a == b); 
    return a; 
} 
+0

'extern" C "' funkcje mogą przyjmować typy C++ (takie jak instancje klasy) jako argumenty, chociaż prawda? Nie sądzę, że "extern" C "' sprawia, że ​​parametry funkcji są magicznie "nie-C++". –

+0

Być może rzeczy takie jak endianness mogą się różnić dla danego systemu między C i C++? To jedyna rzecz, o której teraz mogę myśleć. –

+0

@SethCarnegie: Myślę, że problem polega właśnie na tym, że C++ mówi, że wskaźniki funkcji C i C++ są różnymi typami. Ponieważ nie można wywołać funkcji za pomocą wskaźnika, którego typ jest inny niż zadeklarowany typ, uważam, że wynika z tego, że nie można wywołać funkcji C++ za pomocą wskaźnika "extern C". Chcę się mylić, ale wydaje się, że istnieje zgoda co do tego (zobacz link zamieszczony w pytaniu). –

Odpowiedz

7

Nie znam żadnej platformy, gdzie ABI jest inny, ale nawet jeśli C i C++ konwencje telefoniczne są takie same, C++ norma wymaga kompilatora wydać diagnostyczny dla programu. Wskaźnik do funkcji-z-C-język-podnośnik jest inny typ na wskaźnik do funkcji-z-C++ - language-wiązanie, więc powinieneś być w stanie przeładowywać run() tak:

extern "C" int run(int (*f)(int), int x) { return f(x); } 
extern "C++" int run(int (*f)(int), int x) { return f(x); } 

Teraz, gdy wywołasz run(times), powinien wywołać drugi, więc to, że pierwszy nie jest wywoływalny (nie ma konwersji z wskaźnika na funkcję z łączem języka C do wskaźnika do funkcji -with-C++ - language-linkage), więc oryginalny kod powinien spowodować diagnostykę kompilatora. Większość kompilatorów robi to jednak źle, np. http://gcc.gnu.org/bugzilla/show_bug.cgi?id=2316

N.B. Solaris kompilator ma zdiagnozować niezgodne powiązań językowych, jako ostrzeżenie:

"t.c", line 11: Warning (Anachronism): Formal argument f of type extern "C" int(*)(int) in call to run(extern "C" int(*)(int), int) is being passed int(*)(int).

Jeśli przeciążenie run z funkcją extern "C++" to poprawnie nazywa extern "C++" jeden dla run(times).

+0

Gdzie Standard wymaga, aby kompilator wystawił diagnozę, nie mówiąc już o zatrzymaniu kompilacji? –

+1

@BenVoigt: To: "Dwa typy funkcji z różnymi połączeniami językowymi są odrębnymi typami, nawet jeśli są poza tym identyczne" oznaczałoby, że wywołanie 'run' jest błędne. –

+0

@ n.m .: Standard wyraźnie unika definiowania tego zachowania, co oznacza, że ​​implementacje mogą dokumentować dobrze zdefiniowane zachowanie (ale nie są do tego wymagane). Typy są różne, tak. Standard nie zabrania jednak niejawnej konwersji specyficznej dla implementacji, o ile konwersja ma gorszą pozycję niż konwersja tożsamości, tak więc przeciążanie nadal działa. –

Powiązane problemy