variadic template



  • Die gute alte Laufzeitpolymorphie ...

    ... also irgendwie so ...

    class abstract_callable
    {
    public:
        virtual ~abstract_callable() {}
        virtual void run() = 0; ///< MUST only be called ONCE!
    };
    
    template<int...>
    struct int_pack {};
    
    template<class FunctorType, class TupleType, class Indices>
    class callable;
    
    template<class F, class...T, int...I>
    class callable<F,std::tuple<T...>,int_pack<I...>>
        : public abstract_callable
    {
        F fun;
        std::tuple<T...> params;
    public:
        template<class A, class...Args>
        explicit callable(A&& a, Args&&...args)
            : fun(std::forward<A>(a))
            , params(std::forward<Args>(args)...)
        {}
    
        void run()
        { fun(std::forward<T>(std::get<I>(params))...); }
    };
    
    template<class F, class...Args>
    std::unique_ptr<abstract_callable> make_callable(F&& fun, Args&&...args)
    {
        using std::forward;
        using std::unique_ptr;
        using std::make_tuple;
        typedef typename std::decay<F>::type fun_type;
        typedef decltype(make_tuple(forward<Args>(args)...)) tuple_type;
        typedef typename make_indices<sizeof...(Args)>::type indices_type;
        typedef callable<fun_type,tuple_type,indices_type> call_type;
        unique_ptr<abstract_callable> ac (
            new call_type(forward<F>(fun),forward<Args>(args)...) );
        return ac;
    }
    
    extern "C" void free_thread_func(void *user_data)
    {
        std::unique_ptr<abstract_callable> ac (static_cast<abstract_callable*>(user_data));
        try {
            ac->run();
        } catch (...) {
            // do something sensible here
        }
    }
    

    (ungetestet)

    Da wo der Thread tatsächlich gestartet wird muss man natürlich den rohen Zeiger aus dem von make_callable zurückgegebenen unique_ptr per release befreien, damit das Objekt nicht schon wieder zerstört wird, bevor der Thread überhaupt zum Zuge kommt.



  • Sone schrieb:

    Mach doch sowas in der Richtung:

    ... std::bind ...
    

    Wenn du std::bind dafür verwendest, ist es dir nicht Möglich Objekte von move-only Typen an einen Thread "per value" zu übergeben, da bind die gebundenen Parameter als Lvalue übergibt; denn man könnte den Funktor ja mehrmals ausführen und beim zweiten Mal soll es immer noch "funktionieren". std::thread fürht das Ding aber garantiert höchstens nur einmal aus. Es darf also gebundene Parameter als Rvalue übergeben.



  • und da ich den api-aufruf auch ganz gern im source-file hätte, fehlt mir schon der ansatz. 😕



  • unskilled schrieb:

    und da ich den api-aufruf auch ganz gern im source-file hätte, fehlt mir schon der ansatz. 😕

    Der API-Aufruf findet doch aber in einem Funktionstemplate statt? Und das kannste ja nicht in Source&Header aufteilen...



  • Zu CreateThread mal ein kleines Zitat aus der MSDN:

    A thread in an executable that calls the C run-time library (CRT) should use the _beginthreadex and _endthreadex functions for thread management rather than CreateThread and ExitThread; this requires the use of the multithreaded version of the CRT. If a thread created using CreateThread calls the CRT, the CRT may terminate the process in low-memory conditions.



  • krümelkacker schrieb:

    Sone schrieb:

    Mach doch sowas in der Richtung:

    ... std::bind ...
    

    Wenn du std::bind dafür verwendest, ist es dir nicht Möglich Objekte von move-only Typen an einen Thread "per value" zu übergeben, da bind die gebundenen Parameter als Lvalue übergibt; denn man könnte den Funktor ja mehrmals ausführen und beim zweiten Mal soll es immer noch "funktionieren". std::thread fürht das Ding aber garantiert höchstens nur einmal aus. Es darf also gebundene Parameter als Rvalue übergeben.

    Stimmt, fällt mir auf. Das hält seine Parameter in einem std::tuple...
    Wie wärs mit einem Gefrickel mit std::reference_wrapper ? 😃
    Edit: Nope. Temporäre Objekte können ja mit std::reference_wrappe r nicht gehalten werden, und sowieso...



  • Sone schrieb:

    unskilled schrieb:

    und da ich den api-aufruf auch ganz gern im source-file hätte, fehlt mir schon der ansatz. 😕

    Der API-Aufruf findet doch aber in einem Funktionstemplate statt? Und das kannste ja nicht in Source&Header aufteilen...

    und weil ich denke, dass iwer nen vorschlag hat, wie es doch geht, habe ich hier gepostet...



  • unskilled schrieb:

    und weil ich denke, dass iwer nen vorschlag hat, wie es doch geht, habe ich hier gepostet...

    siehe meinen ersten Beitrag.



  • Warum baust du std::function nach, kk?



  • krümelkacker schrieb:

    unskilled schrieb:

    und weil ich denke, dass iwer nen vorschlag hat, wie es doch geht, habe ich hier gepostet...

    siehe meinen ersten Beitrag.

    ahhh - ganz übersehen. danke : >

    bb



  • Kellerautomat schrieb:

    Warum baust du std::function nach, kk?

    Tue ich gar nicht. std::thread kannst du weder mit std::bind noch mit std::function implementieren -- jedenfalls nicht ohne größere Umwege. Der Standard verlangt, dass bei std::thread Rvalue-Parameter als solche weitergereicht werden.

    Edit: Gut, man könnte std::function doch verwenden ... aber ich wüsste nicht, was das bringen sollte außer einer zusätzlichen Indirektion, auf die ich gern verzichten würde ...



  • [quote="Sone"]

    template<typename Callable>
    DWORD WINAPI dummyFunc(LPVOID ptr)
    {
            (*reinterpret_cast<Callable*>(ptr))();
            return 0;
    }
    
    struct thread
    {
            template<typename Callable, typename ... argTs>
            thread(Callable&& callable, argTs&&... args)
            {
                    auto bound = std::bind(std::forward<Callable>(callable), std::forward<argTs>(args)...);
                    auto ptr = dummyFunc<decltype(bound)>;
    
                    mHandle = CreateThread(nullptr, 0, ptr, &bound, 0, nullptr);
    

    Oh, ich sehe gerade, dass du einen Zeiger auf ein automatisches Objekt übergibst. Das Objekt "bound" verschwindet doch aber gleich, wenn der Konstruktor fertig ist. 'Ne Freispeicherallozierung ist da schon praktischer. 😉



  • Edit: Gleich kommt ne gefixte Version.



  • Der reinterpret_cast ist unnoetig.


Anmelden zum Antworten