Click here to Skip to main content
13,762,666 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

13.2K views
24 bookmarked
Posted 29 Jan 2016
Licenced CPOL

C++ 11 Tricks to Call a Slow Function With A Progress Dialog Box

, 29 Jan 2016
Rate this:
Please Sign up or sign in to vote.
One-line call of a slow function with a progress dialog

Introduction

Let's say we have this function:

int x2(int yy, int zz)
    {
    Sleep(5000);
    return yy + zz;
    }

which takes some 5 seconds to complete. We want to show this dialog to the user:

and execute our function in a background thread.

However, it is bothersome to create custom window, different threads, different parameter passing, etc. for all such calls. Here is a way to call any function with any parameter list in an one-line call:

auto i1 = WC<int,int,int>::WaitCall(nullptr, L"Waiting...",x2,10,20);
// after 5 seconds, i1 = 30

 

ToDo

Make it work with references.

Background

We need C++ 11 tuples, variadic templates and so on. Rest assured, it's not that tough!

Using the Code

All is done in the class WC, which has a static member function WaitCall for the user to call:

template <typename R, typename ...Args>
class WC
   {
   ...
   };

This class takes the return value and the arguments to be passed. To implement a function that takes an arbitrary number of arguments, we need something like this:

static R WaitCall(HWND hh, const wchar_t* txt, std::function<R(Args...)> fn, Args... a)
    {
    WAITCALL w;
    w.txt = txt;
    w.f = fn;
    w.tu = ::make_tuple<Args...>(std::forward<Args>(a)...);
    const char* res = "..."; // the resource we have created
    DialogBoxIndirectParam(GetModuleHandle(0), (LPCDLGTEMPLATEW)res, hh, WaitCallDP, (LPARAM)&w);
    return (R)w.rval;
    }

We need the HWND and text to be shown, a function of return value R and arguments Args... Note that we have created a WAITCALL structure to store the information, because we need to show a dialog box so we have to use a structure to pass along all the information the user has provided:

struct WAITCALL
    {
    wstring txt;
    std::function<R(Args...)> f;
    std::tuple<Args...> tu;
    R rval;
    };

Now for the dialog box procedure. It has to process WM_INITDIALOG to start the thread, then a custom message from the thread function when the call is over:

static INT_PTR __stdcall WaitCallDP(HWND hh, UINT mm, WPARAM, LPARAM ll)
    {
    WAITCALL* eel = (WAITCALL*)GetWindowLongPtr(hh, GWLP_USERDATA);
    switch (mm)
        {
        case WM_USER + 1:
            {
            EndDialog(hh, IDOK);
            return 0;
            }
        case WM_INITDIALOG:
            {
            eel = (WAITCALL*)ll;
            SetWindowLongPtr(hh, GWLP_USERDATA, (LONG_PTR)eel);
            SetDlgItemText(hh, 701, eel->txt.c_str());
            SendDlgItemMessage(hh, 901, PBM_SETMARQUEE, true, 0);
            std::thread t(WaitCallDPCall, hh, eel);
            t.detach();
            return 0;
            }
        }
    return 0;
    }

So in WM_INITDIALOG, we start a new thread with the help of a new function, WaitCallDPCall:

static void WaitCallDPCall(HWND hh, WAITCALL* w)
    {
    w->rval = callthefunction(w->f, w->tu);
    SendMessage(hh, WM_USER + 1, 0, 0);
    return;
    }

Now we need to expand the std::tuple we used to store the arguments into an argument pack, so it can be passed to an ordinary function that does not take a tuple. With the aid of some metaprogramming trick:

template <typename F, typename Tuple, bool Done, int Total, int... N>
struct call_impl
    {
    static R callthefunction(F f, Tuple && t)
        {
        return (R)call_impl<F, Tuple, Total == 1 + sizeof...(N), 
        Total, N..., sizeof...(N)>::callthefunction(f, std::forward<Tuple>(t));
        }
    };

template <typename F, typename Tuple, int Total, int... N>
struct call_impl<F, Tuple, true, Total, N...>
    {
    static R callthefunction(F f, Tuple && t)
        {
        UNREFERENCED_PARAMETER(t);
        return (R)f(std::get<N>(std::forward<Tuple>(t))...);
        }
    };

template <typename F, typename Tuple>
static R callthefunction(F f, Tuple && t)
    {
    typedef typename std::decay<Tuple>::type ttype;
    return (R)call_impl<F, Tuple, 0 == std::tuple_size<ttype>::value, 
    std::tuple_size<ttype>::value>::callthefunction(f, std::forward<Tuple>(t));
    }

Note the UNREFERENCED_PARAMETER(t). When no arguments are to be passed, t is empty, and we don't want a nasty compiler warning.

So the entire code is this one:

// Generic template
template <typename R, typename ...Args>
class WC
    {
    private:

        template <typename F, typename Tuple, bool Done, int Total, int... N>
        struct call_impl
            {
            static R callthefunction(F f, Tuple && t)
                {
                return (R)call_impl<F, Tuple, Total == 1 + sizeof...(N), 
                Total, N..., sizeof...(N)>::callthefunction(f, std::forward<Tuple>(t));
                }
            };

        template <typename F, typename Tuple, int Total, int... N>
        struct call_impl<F, Tuple, true, Total, N...>
            {
            static R callthefunction(F f, Tuple && t)
                {
                UNREFERENCED_PARAMETER(t);
                return (R)f(std::get<N>(std::forward<Tuple>(t))...);
                }
            };

        template <typename F, typename Tuple>
        static R callthefunction(F f, Tuple && t)
            {
            typedef typename std::decay<Tuple>::type ttype;
            return (R)call_impl<F, Tuple, 0 == std::tuple_size<ttype>::value, 
            std::tuple_size<ttype>::value>::callthefunction(f, std::forward<Tuple>(t));
            }

        struct WAITCALL
            {
            wstring txt;
            std::function<R(Args...)> f;
            std::tuple<Args...> tu;
            R rval;
            };

        static void WaitCallDPCall(HWND hh, WAITCALL* w)
            {
            w->rval = callthefunction(w->f, w->tu);
            SendMessage(hh, WM_USER + 1, 0, 0);
            return;
            }

        static INT_PTR __stdcall WaitCallDP(HWND hh, UINT mm, WPARAM, LPARAM ll)
            {
            WAITCALL* eel = (WAITCALL*)GetWindowLongPtr(hh, GWLP_USERDATA);
            switch (mm)
                {
                case WM_USER + 1:
                    {
                    EndDialog(hh, IDOK);
                    return 0;
                    }
                case WM_INITDIALOG:
                    {
                    eel = (WAITCALL*)ll;
                    SetWindowLongPtr(hh, GWLP_USERDATA, (LONG_PTR)eel);
                    SetDlgItemText(hh, 701, eel->txt.c_str());
                    SendDlgItemMessage(hh, 901, PBM_SETMARQUEE, true, 0);
                    std::thread t(WaitCallDPCall, hh, eel);
                    t.detach();
                    return 0;
                    }
                }
            return 0;
            }

    public:

        // User calls this
        static R WaitCall(HWND hh, const wchar_t* txt, std::function<R(Args...)> fn, Args... a)
            {
            WAITCALL w;
            w.txt = txt;
            w.f = fn;
            w.tu = ::make_tuple<Args...>(std::forward<Args>(a)...);
            const char* res = "\x01\x00\xFF\xFF\x00\x00\x00\x00\x80\x00\x00\x00\
            x48\x08\x00\x80\x03\x00\x00\x00\x00\x00\x35\x01\x70\x00\x00\x00\x00\x00\
            x00\x00\x08\x00\x90\x01\x00\x01\x4D\x00\x53\x00\x20\x00\x53\x00\x68\x00\
            x65\x00\x6C\x00\x6C\x00\x20\x00\x44\x00\x6C\x00\x67\x00\x00\x00\x00\x00\
            x00\x00\x00\x00\x00\x00\x07\x00\x00\x50\x07\x00\x07\x00\x27\x01\x62\x00\
            xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
            x00\x00\x01\x02\x02\x50\x1F\x00\x15\x00\xF6\x00\x30\x00\xBD\x02\x00\x00\
            xFF\xFF\x82\x00\x53\x00\x74\x00\x61\x00\x74\x00\x69\x00\x63\x00\x00\x00\
            x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x80\x50\x1F\x00\x50\x00\
            xF6\x00\x0E\x00\x85\x03\x00\x00\x6D\x00\x73\x00\x63\x00\x74\x00\x6C\x00\
            x73\x00\x5F\x00\x70\x00\x72\x00\x6F\x00\x67\x00\x72\x00\x65\x00\x73\x00\
            x73\x00\x33\x00\x32\x00\x00\x00\x00\x00\x00\x00";
            DialogBoxIndirectParam(GetModuleHandle(0), (LPCDLGTEMPLATEW)res, hh, WaitCallDP, (LPARAM)&w);
            return (R)w.rval;
            }
    };

The sad thing is that we might also have a void function, so we can't have a "R rval" inside the WAITCALL structure. Therefore, we also have to provide a partial specialization when R is void:

// Partially specialized for R = void
template <typename ...Args>
class WC<void,Args...>
    {
    private:

        template <typename F, typename Tuple, bool Done, int Total, int... N>
        struct call_impl
            {
            static void callthefunction(F f, Tuple && t)
                {
                call_impl<F, Tuple, Total == 1 + sizeof...(N), Total, N..., 
                sizeof...(N)>::callthefunction(f, std::forward<Tuple>(t));
                }
            };

        template <typename F, typename Tuple, int Total, int... N>
        struct call_impl<F, Tuple, true, Total, N...>
            {
            static void callthefunction(F f, Tuple && t)
                {
                UNREFERENCED_PARAMETER(t);
                f(std::get<N>(std::forward<Tuple>(t))...);
                }
            };

        template <typename F, typename Tuple>
        static void callthefunction(F f, Tuple && t)
            {
            typedef typename std::decay<Tuple>::type ttype;
            call_impl<F, Tuple, 0 == std::tuple_size<ttype>::value, 
            std::tuple_size<ttype>::value>::callthefunction(f, std::forward<Tuple>(t));
            }

        struct WAITCALL
            {
            wstring txt;
            std::function<void(Args...)> f;
            std::tuple<Args...> tu;
            };

        static void WaitCallDPCall(HWND hh, WAITCALL* w)
            {
            callthefunction(w->f, w->tu);
            SendMessage(hh, WM_USER + 1, 0, 0);
            return;
            }

        static INT_PTR __stdcall WaitCallDP(HWND hh, UINT mm, WPARAM, LPARAM ll)
            {
            WAITCALL* eel = (WAITCALL*)GetWindowLongPtr(hh, GWLP_USERDATA);
            switch (mm)
                {
                case WM_USER + 1:
                    {
                    EndDialog(hh, IDOK);
                    return 0;
                    }
                case WM_INITDIALOG:
                    {
                    eel = (WAITCALL*)ll;
                    SetWindowLongPtr(hh, GWLP_USERDATA, (LONG_PTR)eel);
                    SetDlgItemText(hh, 701, eel->txt.c_str());
                    SendDlgItemMessage(hh, 901, PBM_SETMARQUEE, true, 0);
                    std::thread t(WaitCallDPCall, hh, eel);
                    t.detach();
                    return 0;
                    }
                }
            return 0;
            }

    public:

        // User calls this
        static void WaitCall(HWND hh, const wchar_t* txt, std::function<void(Args...)> fn, Args... a)
            {
            WAITCALL w;
            w.txt = txt;
            w.f = fn;
            w.tu = ::make_tuple<Args...>(std::forward<Args>(a)...);
            const char* res = "\x01\x00\xFF\xFF\x00\x00\x00\x00\x80\x00\x00\
            x00\x48\x08\x00\x80\x03\x00\x00\x00\x00\x00\x35\x01\x70\x00\x00\x00\
            x00\x00\x00\x00\x08\x00\x90\x01\x00\x01\x4D\x00\x53\x00\x20\x00\x53\
            x00\x68\x00\x65\x00\x6C\x00\x6C\x00\x20\x00\x44\x00\x6C\x00\x67\x00\
            x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x50\x07\x00\x07\
            x00\x27\x01\x62\x00\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\
            x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x02\x50\x1F\x00\x15\x00\xF6\
            x00\x30\x00\xBD\x02\x00\x00\xFF\xFF\x82\x00\x53\x00\x74\x00\x61\x00\
            x74\x00\x69\x00\x63\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
            x00\x09\x00\x80\x50\x1F\x00\x50\x00\xF6\x00\x0E\x00\x85\x03\x00\x00\
            x6D\x00\x73\x00\x63\x00\x74\x00\x6C\x00\x73\x00\x5F\x00\x70\x00\x72\
            x00\x6F\x00\x67\x00\x72\x00\x65\x00\x73\x00\x73\x00\x33\x00\x32\x00\
            x00\x00\x00\x00\x00\x00";
            DialogBoxIndirectParam(GetModuleHandle(0), (LPCDLGTEMPLATEW)res, hh, WaitCallDP, (LPARAM)&w);
            }
    };

Enjoy your one-line calls!

History

  • 30-01-2016: First release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Michael Chourdakis
Engineer
Greece Greece
I'm working in C++, PHP , Java, Windows, iOS and Android.

I 've a PhD in Digital Signal Processing and Artificial Intelligence and I specialize in Pro Audio and AI applications.

My home page: http://www.michaelchourdakis.com

You may also be interested in...

Pro

Comments and Discussions

 
QuestionHello Pin
Member 1265561126-Jul-16 21:50
memberMember 1265561126-Jul-16 21:50 
Generaluseful Pin
BillW3317-Feb-16 8:31
professionalBillW3317-Feb-16 8:31 
Questionhave you consider to post this as a tip? Pin
Nelek7-Feb-16 0:49
protectorNelek7-Feb-16 0:49 
Suggestionusing with references Pin
dron.plane2-Feb-16 10:12
memberdron.plane2-Feb-16 10:12 
GeneralRe: using with references Pin
Michael Chourdakis2-Feb-16 10:39
mvpMichael Chourdakis2-Feb-16 10:39 
GeneralRe: using with references Pin
dron.plane2-Feb-16 11:13
memberdron.plane2-Feb-16 11:13 
GeneralRe: using with references Pin
Michael Chourdakis3-Feb-16 7:25
mvpMichael Chourdakis3-Feb-16 7:25 
GeneralVery useful Pin
tkontos1-Feb-16 5:48
membertkontos1-Feb-16 5:48 
QuestionUm... Pin
Jeremy Falcon30-Jan-16 8:33
professionalJeremy Falcon30-Jan-16 8:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web06-2016 | 2.8.181112.3 | Last Updated 30 Jan 2016
Article Copyright 2016 by Michael Chourdakis
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid