2014-10-30 10 views
13

Nie widzę żadnego komentarza w standardzie, z wyjątkiem elementów związanych z powiązaniami.Czy "extern" C "` jest częścią typu funkcji?

Chociaż standard nie mówi nic o wywoływaniu konwencji, konwencje wywoływania mogą się różnić między C i C++ w świecie rzeczywistym, więc spodziewałem się, że typy funkcji C i funkcji C++ są różne. Ale wydaje się, że nie, szczególnie w GCC.

#include <type_traits> 

extern "C" { 
    int c_func(int); 
} 

int cpp_func(int); 

static_assert(!std::is_same<decltype(c_func), decltype(cpp_func)>::value, 
       "It should not be the same type"); 

static_assert nie działa, ponieważ GCC uważa, że ​​funkcje mają ten sam typ.

  • Czy extern "C" jest częścią typu funkcji?
  • Jak sprawdzić, czy funkcja używa konwencji wywoływania C lub konwencji wywoływania C++?
+0

C++ obsługuje np. przeciążanie funkcji. Dwie funkcje o różnych typach argumentów, ale o tej samej nazwie - nie można tego zrobić za pomocą linku C, ponieważ nazwa funkcji musi zostać zmanipulowana, aby zachować unikatowość. – keltar

+0

@keltar Tak, masz rację, i nie o to tu pytam. Chodzi o konwencję telefoniczną, a nie o mangling nazwy. Każdy dokument, Q/A o "extern" c "" Znalazłem w Internecie mówi o manglingu nazwy, ale nie konwencji wywołującej. – kukyakya

+1

Ponieważ nie może być użyty na metodach (i dlatego nie może wpływać na to połączenie) - tak, powinien wyłączać jedynie mangling bez modyfikacji konwencji, chyba że wyraźnie określono inaczej (np. Z '__attribute__' polecenie specyficzne dla kompilatora). Oba porównywane typy to 'int (*) (int)'. – keltar

Odpowiedz

16

Norma precyzuje, że język podnośnik jest rzeczywiście właściwością funkcji typu samego:

Wszystkie rodzaje funkcji, nazw funkcji z zewnętrznym łącznikiem i nazw zmiennych z zewnętrznym wiązaniem mieć powiązanie językowe .

W przypadku, że nie było wystarczająco jasne, istnieje notatka (Kopalnia nacisk), który sprawia, że ​​zamierzony sens jednoznaczny:

[Uwaga: Ponieważ podnośnik język jest częścią typ funkcji, podczas pośredniczenia przez wskaźnik do funkcji C , funkcja, do której odnosi się uzyskana l-wartość, jest uważana za funkcję C. - koniec uwaga]

Ponadto

dwa typy funkcyjne z innym języku powiązania są różne typy, nawet jeśli są one inaczej identyczne.

Więc odpowiedź na pierwsze pytanie brzmi:

  • Tak, extern "C" jest częścią rodzaju funkcji.

Jednak większość kompilatorów nie rozróżnia typów funkcji z łączeniem języka C i C++. Jest to na przykład długotrwały błąd w GCC (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=2316; zobacz listę duplikatów). Nie przeczytałem dokładnie wątku, ale wygląda na to, że wiele istniejącego kodu pęknie, jeśli GCC zacznie egzekwować regułę, że tak naprawdę są to różne typy. Prawdopodobnie jest tak również dlatego, że inne kompilatory również nie spełniają standardu.

Biorąc pod uwagę, że odpowiedź na drugie pytanie wydaje się być:

  • Prawdopodobnie nie ma przenośny sposób, aby wykonać tę kontrolę w czasie kompilacji. Oczywiście po tłumaczeniu zawsze możesz wejść i spojrzeć na plik obiektu i sprawdzić, czy nazwa jest zniekształcona czy nie.

Ale w teorii, twoje twierdzenie statyczne ma działać tak, jak powinno. Tak się nie dzieje w praktyce.

Uzupełnienie:

Jeśli moje rozumienie normy jest poprawna, a następnie na przykład według następującego wzoru funkcji

template <typename R, typename... A> 
void f(R(*)(A...)); 

nie może być instancja produkować funkcję, która będzie akceptować wskaźnik do funkcji z łączeniem w języku C jako argumentem, ponieważ typem R(*)(A...) jest "wskaźnik do funkcji z łączem w języku C++ przyjmujący argumenty typów A... i zwracający R ".

Jeśli kompilatory naprawdę działały w ten sposób, łatwo można było zobaczyć, w jaki sposób można ogólnie określić, czy dana funkcja ma powiązanie w języku C lub C++.

Ale ten przykład powinien również wyjaśnić, jak źle powstałby istniejący kod, gdyby kompilatory naprawdę działały w ten sposób.

Powiązane problemy