Adresse einer Referenz



  • Dravere schrieb:

    Wenn ich dich richtig verstehe

    Leider nicht. Ich möchte die Adresse der Stack-Parameter an exceptionGuardedCall() übergeben, auch wenn diese Referenzen sind.

    exceptionGuardedCall() sieht etwa so aus:

    HRESULT exceptionGuardedCall (void (*func)(void), void* first_stack_param, void* last_stack_param)
    {
        try
        {
            // some horrible assembly tricks to push first_stack_param through
            // last_stack_param onto the stack and jump to func
        }
        catch (std::exception& e)
        {
            // set error info via CreateErrorInfo() and SetErrorInfo()
            return 0xA0020001;
        }
        catch (...)
        {
            // set error info via CreateErrorInfo() and SetErrorInfo()
            return 0xA0020002;
        }
    }
    

    (Wer Delphi kennt, sieht, daß ich versuche, den safecall-Mechanismus nachzubauen.)



  • Kann man denn überhaupt davon ausgehen, dass die Parameter alle auf dem Stack liegen? Oo



  • Decimad schrieb:

    Kann man denn überhaupt davon ausgehen, dass die Parameter alle auf dem Stack liegen? Oo

    Nein, kann man nicht. MSVC beherrscht zum Beispiel eine Optimierung namens "custom calling conventions", bei der Parameter auch über ungenutzte Register übergeben werden können.



  • Bei zumindest einigen Compilern dürfte eine Referenz ja als Zeiger übergeben werden. Dann ließe sich die Adresse auf dem Callstack ja aus der Adresse des nächsten Parameters ableiten (&next-sizeof(void*) oder &next+sizeof(void*), keine Ahnung wie das mit den Aufrufkonventionen ist).
    Primitiv etwa so:

    void foo( int& a, int b )
    {
    	int addr_b = (int)(&b);
    	int addr_a = addr_b - sizeof(void*);
    }
    

    Das doofe ist nur, wenn der nächste Parameter auch eine Referenz ist, oder aber der gefragte Parameter der letzte ist. Da könnte helfen, eine Dummy-Funktion mit irgendeinem non-ref-Parameter dahinter zu stellen, die die Adressen rückwärts wieder aufrollt:

    template<class T>
    struct SizeofParam
    {
    	enum
    	{
    		Size = sizeof( 
    				boost::mpl::if_c< boost::is_reference<T>::value,
    				 void*,
    				 T>::type
    		)
    	};
    };
    
    template<class T, class U>
    void dummy_foo( T a, U b, int c )
    {
    	void* addr_c = &c;
    	void* addr_b = (char*)addr_c - SizeofParam<U>::Size;
    	void* addr_a = (char*)addr_b - SizeofParam<T>::Size;
    
    	size_t frame = (char*)addr_c - (char*)addr_a;
    
    	std::cout << "Stackframe von " << addr_a << " bis " << addr_c << "  (" << frame << ")\n";
    }
    
    template<class T, class U>
    void foo1( T a, U b )
    {
    	dummy_foo<T,U>( a, b, 0 );
    }
    
    int main()
    {
    	double i = 5;
    	foo1<double&,char>( i, 20 );
    }
    

    edit: Ne, ist Unsinn. Kann so nicht funktionieren, da bei nem char-Parameter ja oft trotzdem ein int auf den Stack gepusht wird.



  • Das fällt einem dann natürlich in den Debug-Builds bestimmt nicht auf 🙂



  • LordJaxom schrieb:

    MSVC beherrscht zum Beispiel eine Optimierung namens "custom calling conventions", bei der Parameter auch über ungenutzte Register übergeben werden können.

    Darf er das überhaupt, wenn man sich die Adressen der Parameter holt? Bzw wie sehen die dann aus?



  • Decimad schrieb:

    Kann man denn überhaupt davon ausgehen, dass die Parameter alle auf dem Stack liegen? Oo

    Ja, kann man, insbesondere, wenn theFunctionWrapper() im realen Einsatz kein Template mehr ist, sondern als virtual __stdcall-Methode deklariert wird. Als Template habe ich die Funktion oben notiert, damit ersichtlich ist, daß ich nicht eine spezifische, sondern eine allgemein anwendbare Lösung benötige, die damit zurechtkommt, daß ein Parameter _eventuell_ einen Referenzypen hat.

    @Badestrand: dein Ansatz ist nicht schlecht, aber man kann ihn leider in genau dieser Hinsicht kaum verallgemeinern, was in meinem Falle leider notwendig wäre.



  • Wo ich das grad lese:
    Bisher dachte ich,
    Adresse eines Objektes == Adresse einer Referenz auf das Objekt
    scheint aber nicht so zu sein, wenn ich das hier richtig nachvollziehe.
    Sonst müsste ja ein
    theFunction<int> für int und int& gehen...
    oder bringe ich da was durcheinander?

    mfg,
    ein verwirrter branleb 😕



  • Für was für Compiler soll das denn funktionieren?



  • branleb schrieb:

    Adresse eines Objektes == Adresse einer Referenz auf das Objekt
    scheint aber nicht so zu sein, wenn ich das hier richtig nachvollziehe.

    Doch, aus semantischer Perspektive ist das so, d.h., wenn du den &-Operator auf eine Referenz anwendest, bekommst du die Adresse des Objektes, auf das sie verweist.

    Praktisch allerdings sind Referenzen zumindest dort, wo sie Teil einer konsistenten Schnittstelle sind, als Zeiger implementiert. Und mein Problem ist, daß C++ keine Möglichkeit bietet, an ihre Adresse zu kommen.

    Die einzige Möglichkeit, die ich gerade sehe, wäre, die gewünschten Daten mithilfe einer Kombination aus dem Ansatz von Badestrand und dem direkten Zugriff auf den Base-Pointer zu erhalten:

    template <typename T>
        struct ParameterSize
    {
        enum { value = boost::is_reference <T>::value ? sizeof (void*) : sizeof (T) };
    };
    template <typename T0, typename T1, ..., typename Tn>
        HRESULT theFunctionWrapper (T0 p0, T1, p1, ..., Tn pn, TR& pr)
    {
        char* begin = (char*) _EBX + 8; // _EBX + 4 ist die Rücksprungadresse
        char* end   = begin
                    + ParameterSize <T0>::value
                    + ParameterSize <T1>::value
                    + ...
                    + ParameterSize <Tn>::value;
        return exceptionGuardedCall ((void (*)(void)) theRealFunction, begin, end);
    }
    

    Leider ist das außerordentlich häßlich. Aber was bleibt mir übrig 😞

    Badestrand schrieb:

    Für was für Compiler soll das denn funktionieren?

    Windows, x86, möglichst alle, mindestens BCC.

    Grundsätzlich wird die Fallback-Variante meines Safecall-Wrappers unter allen Compilern funktionieren, jedoch für einigen Bloat sorgen (jede einzelne Funktion bekäme dann einen try/catch-Block). Das möchte ich eben für so viele Compiler wie möglich vermeiden.


Anmelden zum Antworten