Frage

Für ein System, ich brauche einen Zeiger auf eine lange dann die lange zurück in den Zeigertyp zu konvertieren. Wie Sie sich vorstellen können, ist dies sehr unsicher. Was ich wollte, ist tun dynamic_cast verwenden, um die Konvertierung zu tun, wenn ich sie gemischt werde ich einen Null-Zeiger erhalten. Diese Seite sagt http://publib.boulder.ibm.com/infocenter/lnxpcomp/v7v91/index.jsp?topic=/com.ibm.vacpp7l.doc/language/ref/clrc05keyword_dynamic_cast.htm

  

Der dynamic_cast Operator führt   Typkonvertierungen zur Laufzeit. Das   dynamic_cast Betreiber garantiert   Umwandlung eines Zeigers auf einer Basis   Klasse zu einem Zeiger auf eine abgeleitete Klasse,   oder die Umwandlung eines lvalue   Bezugnahme auf eine Basisklasse ein   Bezugnahme auf eine abgeleitete Klasse. EIN   Programm kann dadurch eine Klasse verwenden   Hierarchie sicher. Dieser Operator und   die typeid Betreiber bieten Laufzeit   Typinformationen (RTTI) -Unterstützung in   C ++.

und ich möchte einen Fehler erhalten, wenn es null ist, so schrieb ich meine eigene dynamische Umwandlung

template<class T, class T2> T mydynamic_cast(T2 p)
{
    assert(dynamic_cast<T>(p));
    return reinterpret_cast<T>(p);
}

Mit MSVC erhalte ich die Fehlermeldung „Fehler C2681:‚long‘: ungültiger Ausdruckstyp für dynamic_cast“. Es stellt sich heraus, dies wird nur mit Klassen arbeiten, die virtuellen Funktionen haben ... WTF! Ich weiß, dass der Punkt einer dynamischen Besetzung für das oben war / unten Casting Vererbung Problem, aber ich dachte auch, es war die Art Guss Problem dynamisch zu lösen. Ich weiß, ich reinterpret_cast verwenden könnte, aber dass die gleiche Art von Sicherheit nicht garantieren.

Was soll ich überprüfen verwenden, wenn meine Typumwandlung vom gleichen Typ ist? Ich kann die zwei typeid vergleichen, aber ich würde ein Problem hat, wenn ich eine abgeleitete seine Basis typisieren will. Also, wie kann ich dieses Problem lösen?

War es hilfreich?

Lösung

Ich habe ähnliche Dinge zu tun, wenn C ++ DLLs in Anwendungen geschrieben in Sprachen laden, die nur eine C-Schnittstelle unterstützen. Hier ist eine Lösung, die Ihnen einen sofortigen Fehler geben wird, wenn ein unerwarteter Objekttyp in geben wurde. Diese Dinge machen kann viel leichter zu diagnostizieren, wenn etwas schief geht.

Der Trick besteht darin, dass jede Klasse, die Sie als Griff umkippen hat von einer gemeinsamen Basisklasse erbt.

#include <stdexcept>
#include <typeinfo>
#include <string>
#include <iostream>
using namespace std;


// Any class that needs to be passed out as a handle must inherit from this class.
// Use virtual inheritance if needed in multiple inheritance situations.
class Base
{

public:
    virtual ~Base() {} // Ensure a v-table exists for RTTI/dynamic_cast to work
};


class ClassA : public Base
{

};

class ClassB : public Base
{

};

class ClassC
{
public:
    virtual ~ClassC() {}
};

// Convert a pointer to a long handle.  Always use this function
// to pass handles to outside code.  It ensures that T does derive
// from Base, and that things work properly in a multiple inheritance
// situation.
template <typename T>
long pointer_to_handle_cast(T ptr)
{
    return reinterpret_cast<long>(static_cast<Base*>(ptr));
}

// Convert a long handle back to a pointer.  This makes sure at
// compile time that T does derive from Base.  Throws an exception
// if handle is NULL, or a pointer to a non-rtti object, or a pointer
// to a class not convertable to T.
template <typename T>
T safe_handle_cast(long handle)
{
    if (handle == NULL)
        throw invalid_argument(string("Error casting null pointer to ") + (typeid(T).name()));

    Base *base = static_cast<T>(NULL); // Check at compile time that T converts to a Base *
    base = reinterpret_cast<Base *>(handle);
    T result = NULL;

    try
    {
        result = dynamic_cast<T>(base);
    }
    catch(__non_rtti_object &)
    {
        throw invalid_argument(string("Error casting non-rtti object to ") + (typeid(T).name()));
    }

    if (!result)
        throw invalid_argument(string("Error casting pointer to ") + typeid(*base).name() + " to " + (typeid(T).name()));

    return result;
}

int main()
{
    ClassA *a = new ClassA();
    ClassB *b = new ClassB();
    ClassC *c = new ClassC();
    long d = 0; 


    long ahandle = pointer_to_handle_cast(a);
    long bhandle = pointer_to_handle_cast(b);
    // long chandle = pointer_to_handle_cast(c); //Won't compile
    long chandle = reinterpret_cast<long>(c);
    // long dhandle = pointer_to_handle_cast(&d); Won't compile
    long dhandle = reinterpret_cast<long>(&d);

    // send handle to library
    //...
    // get handle back
    try
    {
        a = safe_handle_cast<ClassA *>(ahandle);
        //a = safe_handle_cast<ClassA *>(bhandle); // fails at runtime
        //a = safe_handle_cast<ClassA *>(chandle); // fails at runtime
        //a = safe_handle_cast<ClassA *>(dhandle); // fails at runtime
        //a = safe_handle_cast<ClassA *>(NULL); // fails at runtime
        //c = safe_handle_cast<ClassC *>(chandle); // Won't compile
    }
    catch (invalid_argument &ex)
    {
        cout << ex.what() << endl;
    }

    return 0;
}

Andere Tipps

dynamic_cast kann nur zwischen den Klassen durch Vererbung im Zusammenhang verwendet werden. Für einen Zeiger auf lange oder umgekehrt konvertieren, können Sie reinterpret_cast verwenden. Um zu überprüfen, ob der Zeiger null ist, können Sie assert(ptr != 0). Es ist jedoch in der Regel nicht ratsam, reinterpret_cast zu verwenden. Warum wollen Sie brauchen einen Zeiger auf lange konvertieren?

Eine andere Möglichkeit ist, eine Gewerkschaft zu verwenden:

union  U { 
int* i_ptr_;
long l;
}

Wieder zu Union nur selten benötigt wird.

Beachten Sie, dass in Windows 64, ein Zeiger eine 64-Bit-Größe sein, aber long wird noch eine 32-Bit-Größe sein und Ihr Code ist gebrochen. Zumindest müssen Sie die Wahl von Integer-Typ auf der Plattform basieren. Ich weiß nicht, ob MSVC Unterstützung für uintptr_t hat, die Art in C99 vorgesehen Zeiger zu halten; das wäre die beste Art zu verwenden, wenn es verfügbar ist.

Wie für den Rest, andere haben angesprochen, die, warum und weshalb die von dynamic_cast vs reinterpret_cast ausreichend.

reinterpret_cast ist die richtige Besetzung hier zu verwenden.

Das ist so ziemlich das nur , was es sicher tun können.

reinterpret_cast von einem Zeigertyp in einen Typ T und wieder auf den ursprünglichen Zeigertyp ergibt den ursprünglichen Zeiger. (Unter der Annahme, T ist ein Zeiger oder Integer-Typ, die mindestens so groß wie der ursprüngliche Zeiger-Typ ist)

Beachten Sie, dass von einem Zeiger Typ T reinterpret_cast ist nicht spezifiziert. Es gibt keine Garantien über den Wert des T-Typs, außer , wenn Sie dann reinterpret_cast es wieder auf den ursprünglichen Typ, können Sie den ursprünglichen Wert. So können Sie unter der Annahme, versuchen Sie nicht, irgendetwas mit dem Zwischen long-Wert in Ihrem Fall zu tun, reinterpret_cast ist absolut sicher und tragbar.

Edit: Natürlich ist dies nicht hilft, wenn Sie bei dem zweiten Guss wissen nicht, was die ursprüngliche Art war. In diesem Fall sind Sie geschraubt. Das kann lange nicht möglicherweise in irgendeiner Weise carry Art Informationen darüber, welche Zeiger aus umgewandelt wurde.

Sie können mit reinterpret_cast zu einem integralen Typ umgewandelt und zurück zu dem Zeigertyp. Wenn das Integral Typ, der groß genug ist, um den Zeiger-Wert zu speichern, wird diese Umwandlung nicht den Zeigerwert geändert werden.

Wie andere schon sagen, ist es nicht Verhalten definiert auf einer nicht-polymorphe Klasse verwenden dynamic_cast (außer, wenn Sie eine upcast tun, die ohnehin implizit ist und hier außer Acht gelassen werden), und es funktioniert auch nur auf Zeiger oder Verweise. Nicht auf ganzzahlige Typen.

Sie eine bessere Nutzung ::intptr_t gefunden in auf verschiedenen Posix-Systemen. Sie können diesen Typ als Zwischentyp verwenden Sie zu werfen.

In Bezug auf Ihre Überprüfung, ob die Konvertierung erfolgreich sein wird, können Sie sizeof verwenden:

BOOST_STATIC_ASSERT(sizeof(T1) >= sizeof(T2));

wird bei der Kompilierung fehlschlagen, wenn die Konvertierung nicht durchgeführt werden kann. Oder fahren Sie mit dieser Bedingung verwenden behauptet, und es wird stattdessen zur Laufzeit geltend zu machen.

Achtung: Dies verhindert, dass Sie nicht aus T* Gießen zurück intptr_t So mit U einem anderen Typ als T. U*, das Sie nur garantiert die Besetzung nicht den Wert des Zeigers ändern wenn Sie von T* gießen intptr_t T* und zurück. (Danke an Nicola Hinweis auf Sie einen anderen Schutz erwarten kann).

Was Sie tun möchten, klingt wie eine wirklich schlechte und gefährliche Idee, aber wenn Sie es tun muss (dh Sie in einem Altsystem oder auf Hardware arbeiten, die Sie wissen, wird sich nie ändern), dann würde ich vorschlagen, die Umhüllung Zeiger in einer Art einfacher Struktur, die zwei Elemente enthält: 1) einen Hohlraum Zeiger auf Ihre Objektinstanz und eine Zeichenfolge, enum oder eine andere Art von eindeutiger Kennung, die Ihnen sagen, was die ursprüngliche Leere werfen * zu. Hier ist ein Beispiel dafür, was ich meinte, (Anmerkung: Ich habe nicht die Mühe dieser Prüfung so dass es darin syntaktische Fehler sein kann):

struct PtrWrapper {
  void* m_theRealPointer;
  std::string m_type;
};

void YourDangerousMethod( long argument ) {

   if ( !argument ) 
     return;

   PtrWrapper& pw = *(PtrWrapper*)argument;

   assert( !pw.m_type.empty() );

   if ( pw.m_type == "ClassA" ) {
     ClassA* a = (ClassA*)pw.m_theRealPointer;
     a->DoSomething();
   } else if (...) { ... }

}

dynamic_cast<> ist ein gegossenes verwendet werden sollte nur auf Cabrio Typen (in dem polymorphen Sinne). Erzwingen der Abguss eines pointer zu einem long (litb richtig static_assert schlägt die Kompatibilität der Größe zu gewährleisten) alle Informationen über die Typ des Zeigers verloren. Es gibt keine Möglichkeit, eine safe_reinterpret_cast<> zu implementieren, um die Zeiger zurück zu erhalten. Sowohl Werte und Typen

Um zu klären, was ich meine:

struct a_kind {}; 
struct b_kind {}; 

void function(long ptr) 
{} 

int 
main(int argc, char *argv[]) 
{ 
    a_kind * ptr1 = new a_kind; 
    b_kind * ptr2 = new b_kind;

    function( (long)ptr1 );
    function( (long)ptr2 );

    return 0;
}

Es gibt keine Möglichkeit für function() die Art des Zeigers zu bestimmen, übergeben und „unten“ wirft sie auf die richtige Art, es sei denn:

  • die lange von einem Objekt mit einigen Informationen des Typs gewickelt wird.
  • der Typ selbst ist in das referenzierte Objekt codiert.

Beide Lösungen sind hässlich und sollten vermieden werden, da RTTI Surrogate sind.

auch, eine bessere Nutzung size_t statt einer langen -. Ich denke, diese Art mit der Größe des Adressraums vereinbar gewährleistet ist

Sobald Sie einen Zeiger auf eine lange werfen beschlossen, Sie warf Typsicherheit in den Wind.

dynamic_cast wird verwendet, um Guss nach oben und unten ein Ableitungsbaum. Das heißt, von einem Basisklassenzeiger auf einen abgeleitete Klasse-Zeiger. Wenn Sie:

class Base
{
};

class Foo : public  Base
{
};

class Bar : public Base
{
};

Sie können auf diese Weise verwenden dynamic_cast ...

Base* obj = new Bar;

Bar* bar = dynamic_cast<Bar*>(obj); // this returns a pointer to the derived type because obj actually is a 'Bar' object
assert( bar != 0 );

Foo* foo = dynamic_cast<Foo*>(obj);  // this returns NULL because obj isn't a Foo
assert( foo == 0 );

... aber man kann nicht dynamische Umwandlung verwenden, um einen aus einer Ableitungsbaum gossen. Sie benötigen reinterpret_cast oder C-Casts dafür.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top