2009-08-20 10 views
6

Chcę ponownie interpretować rzut wskaźnika funkcji na zmienną void *. Typ wskaźnika funkcji będzie typu Class* (*)(void*).reinterpret_cast do void * nie działa z wskaźnikami funkcji

Poniżej znajduje się przykładowy kod,

class Test 
{ 
    int a; 
}; 

int main() 
{ 
    Test* *p(void **a); 
    void *f=reinterpret_cast<void*>(p);  
} 

Powyższy kod działa dobrze z Visual Studio kompilatory/x86. Ale z kompilatorem ARM daje błąd kompilacji. Nie wiem dlaczego.

Error: #694: reinterpret_cast cannot cast away const or other type qualifiers

czytam wyjaśnienia w Casting a function pointer to another type

byłem zaniepokojony poniżej wyjaśnienia.

Casting between function pointers and regular pointers (e.g. casting a void (*)(void) to a void*). Function pointers aren't necessarily the same size as regular pointers, since on some architectures they might contain extra contextual information. This will probably work ok on x86, but remember that it's undefined behavior.

Jak zrobić takie konwersje z void (*)(void*) -> void* tak skutecznie, że conajmniej kompiluje prawie takie same w większości kompilatorów?

+0

Powinieneś przynajmniej umieścić w twierdzeniu, że 'sizeof (void *)> = sizeof (Test ** (*) (void **))'. Jeśli to potwierdzenie nie powiedzie się, to oczywiście nie ma sposobu na zapisanie wskaźnika funkcji w 'void *'. Jeśli asercja nie zawodzi, nadal nie gwarantuje niczego, ale przynajmniej możliwe, że obsada może działać. –

+0

Należy zauważyć, że p rzeczywiście ma typ Test ** (void **) i jest funkcją, a nie wskaźnikiem funkcji. Umieszczenie spacji w "' Test * * '" nie wpływa na parsowanie. – MSalters

Odpowiedz

7

reinterpret_cast nie można użyć do rzutowania wskaźnika na funkcję na void*. Chociaż istnieje kilka dodatkowych rzeczy, które może wykonywać rzutowanie C, które nie są dozwolone przez kombinację rzutów statycznych, reinterpretacyjnych i stałych, ta konwersja nie jest jedną z nich.

W C rzutowanie jest dozwolone, ale jego zachowanie nie jest zdefiniowane (tzn. Nawet podróż w obie strony nie jest gwarantowana).

Niektóre funkcje POSIX wymagają konwersji, aby były dobrze użyteczne.

Grałem z kilku kompilatorów Mam tutaj:

  • żaden zapobiec C obsadę, nawet w najwyższym trybie zgodności. Niektóre dają ostrzeżenie w zależności od ostrzeżenia i poziomu zgodności, inne nie ostrzegały o tym, co próbowałem.
  • reinterpret_cast był błędem w niektórych kompilatorach, nawet w bardziej zrelaksowanym poziomie, podczas gdy inne zaakceptowały go we wszystkich przypadkach, nie dając żadnego ostrzeżenia.

W ostatnim dostępnym projekcie dla C++ 0X, podpora reinterpret_cast między wskaźnikami funkcji i wskaźnikami obiektów jest warunkowo obsługiwana.

Należy pamiętać, że jeśli to ma sens, czy nie, będzie zależało od celu bardziej niż kompilator: przenośny kompilator taki jak gcc będzie miał zachowanie narzucone przez docelową architekturę i prawdopodobnie ABI.

Jako drugi mają uczynić uwagę,

Test* *p(void **a); 

definiuje funkcję, nie wskaźnik do funkcji. Ale funkcja wskaźnika do funkcji niejawnej konwersji jest tworzona dla argumentu reinterpret_cast, więc to co reinterpret_cast dostaje to Test** (*p)(void** a).

Dziękuję Richardowi, który sprawia, że ​​wracam do tematu bardziej dogłębnie (dla przypomnienia, pomyliłem się myśląc, że wskaźnikiem do wskazywania wskaźnika na obiekt był jeden przypadek, w którym obsada C zezwalała na coś nieautoryzowanego przez C++ na kombinacje rzutów).

+0

C cast działało idealnie. Dzięki. :) – vprajan

+0

(Prawie -1) Nie sądzę, że to prawda, czy możesz podać referencję? Rozumiem, że obsada stylu C definiowana jest w kategoriach nowych stylów obsady. Dlatego, jeśli nie można tego zrobić za pomocą reinterpret_cast, nie jest to możliwe również w przypadku rzutowania w stylu C. –

0

nie powinien być Test* *p(void **a); być Test* (*p)(void **a)?

Może to twój problem?

+0

Test * * p (void ** a) również rozwiązuje się, wpisując Test * (*) (void **) .. więc uważam, że obie reprezentacje są prawidłowe. Jestem tego pewny, ponieważ próbowałem w ten sposób też. – vprajan

3

Jeśli szukasz po prostu do przechowywania różnych typów wskaźnika funkcji na liście, a następnie można oddać do wspólnego typu wskaźnik funkcji:

class Test { 
    int a; 
}; 

int main() 
{ 
    Test* *p(void **a); 
    void (*f)()=reinterpret_cast<void (*)()>(p); 
} 

Dotyczy to zrobić poprzez reinterpret_cast (5.2.10/6):

A pointer to a function can be explicitly converted to a pointer to a function of a different type. The effect of calling a function through a pointer to a function type (8.3.5) that is not the same as the type used in the definition of the function is undefined. Except that converting an rvalue of type "pointer to T1" to the type "pointer to T2" (where T1 and T2 are function types) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified.

7

reinterpret_cast może być używany tylko do

  • add constness
  • konwertować wskaźnik do integralnego rodzaju wystarczająco duże, aby trzymać go iz powrotem
  • konwertować wskaźnik do funkcji do wskaźnika do funkcji innego typu
  • konwertować wskaźnik do obiektu do wskaźnika do obiektu inny rodzaj
  • konwersji wskaźnik funkcji składowej na wskaźnik do funkcji składowej innego typu
  • konwersji wskaźnik obiektu użytkownik do wskaźnika do obiektu użytkownik różnego rodzaju
  • i reinterpret_cast<T&>(x) jest równoważna *reinterpret_cast<T*>(&x) (przy użyciu wbudowanego & i *) za każdym razem, gdy druga obsada jest możliwa przy użyciu powyższych reguł.

(patrz rozdział 5.2.10 normy)

Oznacza to w szczególności, że odlew ze wskaźnikiem funkcjonować do void * nie jest możliwe, ale można oddać go do void(*)().


EDIT (2017): Odpowiedź powyżej jest poprawna tylko dla C++ 03. W C++ od 11 do C++ 17 jest zdefiniowana implementacja, jeśli dozwolone są konwersje między wskaźnikami funkcji a void *. Zwykle ma to miejsce w systemach kompatybilnych z POSIX, ponieważ zadeklarowano, że dlsym() zwróci void *, a klienty oczekują od reinterpret_cast poprawnego typu wskaźnika.

Aby uzyskać pełną listę dozwolonych konwersji, zobacz cppreference.com.

2

Inne wskazał, że nie może tego zrobić obsadę (silnie mówiąc, casting do void* cokolwiek używając reinterpret_cast również nie jest dozwolony. - ale cicho tolerowana przez kompilatory static_cast ma być tu użyty).

zwykle wykonaj następujące czynności, które wykonuje typu kalambur i jest zalecany sposób to zrobić, zgodnie z podręcznika z dlopen (co stanowi około robi coś przeciwnego - casting zvoid*do funkcji wskaźnik). Podając adres wskaźnika funkcji otrzymasz wskaźnik danych: Wskaźnik do wskaźnika funkcji. Pozwoli to przesłać go do void*. Udaje, że wskazuje na void* (zamiast wskaźnika funkcji), a następnie odczytuje go.

Test* (*pF)(void **a); 
void *p = *(void**)(void*)&pF; 

Pośrednik oddanych do void* czyni to równoważne użyciu dwóch static_cast „s wewnętrznie, i sprawia, że ​​GCC być spokojny o ostrzeżenie o typu słów. Korzystanie z C++ styl rzuca, to wygląda jak połączenie dwóch static_cast „s

void *p = *static_cast<void**>(static_cast<void*>(&pF)); 

Okazało się, że przy użyciu tej techniki, GCC automatycznie zauważa gdy lewy i prawy typy różnią się wielkością i wypluł ostrzeżenie, że walizka. Nie trzeba dodawać, że tak jak w przypadku wszystkich technik, które próbują obejść to ograniczenie, jest to niezdefiniowane zachowanie.


Jeśli masz funkcję, a chcesz void* wskazując go, można zrobić to wszystko w jednej linii, ale to nieco kłopotliwe w składni. Oto jak można to zrobić, ale nie jest to zalecane, jeśli masz problemy, aby ją przeczytać - można jednak używać go wewnątrz makro

// using the function declaration you provided 
Test** pF(void **a); 
void *p = *(void**)(void *) &(Test** (* const&)(void **a))&pF; 

Sztuką rozpocząć będąc w stanie to zrobić, ale typ jest przekształcenie tymczasowego wskaźnika funkcji na referencję lwartościową na const, którą można pobrać z adresu, a następnie postępować jak powyżej.

Używanie wyraźnych rzutów w stylu C++ static_cast wygląda na bardziej skomplikowane, ponieważ trzeba wziąć pod uwagę tę konsternację. Odtworzenie w stylu C automatycznie się z tym uporało. Baw się dobrze!

int main() { 
    Test** pF(void **a); 
    void *p = *static_cast<void* const*>(
     static_cast<void const*>(
     &static_cast<Test** (* const&)(void **a)>(&pF))); 
} 
+0

Dzięki za to - kod sprawia, że ​​moje gałki oczne pełzają, ale wygląda na to, że działają poprawnie i unikają ostrzeżeń/błędów nawet przy maksymalnych poziomach ostrzeżeń w gcc i clang. – snogglethorpe

Powiązane problemy