0

In meiner App möchte ich ein Parameterpaket über eine Legacy-Funktionssignatur übergeben und die Werte ändern. Hier ist Code, der meine Frage mit meinen Versuchen als Kommentare zeigt:Übergabe eines Parameterpakets über eine Legacy-Funktionssignatur mit forward_as_tuple

#include <tuple> 
#include <cassert> 

void LegacySignature(void* param); 

template< typename... ArgsT > 
// using ???; // attempt: can 'template alias' or 'using declaration' make the pack's type visible so I can use it inside the LegacyFunction? 
void MyFunc(ArgsT&... args) 
{ 
    auto userArgsTuple = std::forward_as_tuple< ArgsT&... >(args...); 

    LegacySignature(&userArgsTuple); 
} 

void LegacySignature(void* param) 
{ 
// auto userArgsTuple = reinterpret_cast<???>(param); // attempt: how can I get the parameter pack's type declared so I can use it here? 

    // do something with the params like change num to 44 and tf to true; 
    //userArgsTuple->num = 44; // desired functionality 
    //userArgsTuple->tf = true; // desired functionality 
} 

int main() 
{ 
    int num { 33 }; 
    bool tf { false }; 
    MyFunc(num, tf); 
    assert(num == 44 && tf == true); 

    return 0; 
} 

Gibt es eine Möglichkeit die Parameter packen einen deklarierbaren lvalue zu machen?

+0

Als eine Vermutung, sie wollen, dass Sie einen Rückruf weitergeben. Der Rückruf nimmt ein "void *" und einige andere Argumente, die sie zur Verfügung stellen. Sie möchten einige Daten durch das 'void *' übergeben, damit es am anderen Ende an Sie zurückgegeben wird. Sie speichern Ihren Callback oder Ihre 'void *' nicht länger als einen festen Bereich, den Sie kontrollieren können. Ist das richtig? Frage: Wird der 'void *' als erster oder letzter Parameter an die Callback-Funktion übergeben? – Yakk

+0

Die Verwendung von 'void *' war lediglich mein Versuch, die eigentliche Signatur als einen neutralen Typ für die Diskussion darzustellen. Tatsächlich ist der eigentliche Signaturparameter das ehrwürdige 'LPARAM', das ein' long' ist. Wie MS angibt, sind sowohl "WPARAM" als auch "LPARAM" "Typen, die zum Übergeben und Zurückgeben von polymorphen Werten verwendet werden". Daher werden in meiner Frage keine Rückrufe übergeben oder aufgerufen. Mein Interesse liegt direkt in einem besseren Verständnis darüber, wie man eine beliebige Anzahl von Argumenten über ein solches Legacy-Konstrukt weiterleitet, wobei diese Parameter wertveränderbare Referenzen in einem intelligenten Zeiger sind. – rtischer8277

+0

FYI, ich löste das Problem von Callbacks über Legacy-Signaturen mit 'future'. Siehe StackOverflow-Problem [Wie kann ich shared_ptr mit PostThreadMessage verwenden?] (Http://stackoverflow.com/questions/25667226) und nach der Antwort suchen, die mit beginnt * @ Remy Lebeau und @ rtischer8277 haben bisher zwei Antworten zu meinem ursprünglichen Beitrag eingereicht ... *. – rtischer8277

Antwort

0

Der folgende Code behebt den Beispielcode, sodass er die Frage beantwortet, wie ein Parameterpaket mithilfe von forward_as_tuple über eine Legacy-Funktionssignatur übergeben wird.

#include <tuple> 
#include <cassert> 
#include <memory> 
#include <functional> 

#define ARGSET int, bool 

void LegacySignature(long* param); // ie, LPARAM 

template< typename... ArgsT > 
struct MyParams 
{ 
    MyParams(ArgsT... args) : rvalRefs { std::forward_as_tuple(args...) } {} // The resulting forward_as_tuple tuple has rvalue reference data members 
    std::tuple<ArgsT...> rvalRefs; 
}; 


void LegacySignature(long* legSigParam) 
{ 
    auto userArgsTuple(reinterpret_cast< MyParams<ARGSET>* >(legSigParam)); 

    // do something with the params like change num to 44 and tf to true; 
    std::get<0>(userArgsTuple->rvalRefs) = 44; // index types can probably be worked out using enums 
    std::get<1>(userArgsTuple->rvalRefs) = true; 
} 

int main() 
{ 
    int num { 33 }; 
    bool tf { false }; 
    MyParams<ARGSET> myParams(num, tf); 

    std::unique_ptr< MyParams<ARGSET> > legSigParamPtr = std::make_unique< MyParams<ARGSET> >(myParams); 
    LegacySignature((long*)legSigParamPtr.get()); 
    assert(std::get<0>(legSigParamPtr->rvalRefs) == 44 && std::get<1>(legSigParamPtr->rvalRefs) == true); 

    return 0; 
} 
0

Ich nehme an, was Sie wollen, ist ein Funktionszeiger auf Ihre Legacy-Signatur.

Hier ist ein C++ 11 Ansatz. wir diese

template<class Sig, class F> 
struct magic_callback_t; 

template<class R, class...Args, class F> 
struct magic_callback_t<R(Args...), F> { 
    F f; 
    void* pvoid() const { return this; } 
    using result_sig = R(*)(void*, Args...); 
    result_sig pfunc() const { 
    return [](void* pvoid, Args...args)->R{ 
     auto* self = static_cast<magic_callback_t*>(pvoid); 
     return (self->f)(std::forward<Args>(args)...); 
    }; 
    } 
}; 
template<class Sig, class F> 
magic_callback_t<Sig, F> magic_callback(F&& f) { 
    return {std::forward<F>(f)}; 
} 

Jetzt gerade tun:

auto callback = magic_callback([&](){ 
    // use whatever as if we where in the enclosing scope 
}); 

void(*)(void*) legacy_ptr = callback.pfunc(); 
legacy_ptr(callback.pvoid()); 

rufen die Lambda Sie magic_callback geben.

Wenn Sie Sachen als Tupel speichern möchten, können Sie. Erfasse einfach das Tupel im Lambda und benutze dann std::get, um es im Körper des Lambda aufzurufen. Verwenden Sie mutable, wenn Sie möchten, dass es veränderbar ist.

+0

Hervorragende Rückruf-Lösung. Mein grundlegendes Bedürfnis besteht jedoch darin, Threads zu wechseln, synchron eine bestehende Funktion im Ziel-Thread auszuführen, vielleicht mit übergebenen Parametern, und die Werte synchron zurückzugeben. Meine * SendThreadMessage * -Lösung, auf die ich Sie hingewiesen habe, tut dies, bis auf die Übergabe mehrerer Parameter. Der Ansatz der willkürlichen Parameterübergabe durch Referenz, wie im Beispielcode beschrieben, ist ein besseres Design. Callback-Lösungen sind leider asynchron. Ich könnte immer einen rohen PTR an einen einzigartigen oder geteilten Obj weitergeben, aber das würde die Arbeit der Anrufer erschweren. – rtischer8277

Verwandte Themen