Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C++

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

Rate me:
Please Sign up or sign in to vote.
4.37/5 (18 votes)
29 Jan 2016CPOL2 min read 21.2K   24   10
One-line call of a slow function with a progress dialog

Introduction

Let's say we have this function:

C++
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:

Image 1

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:

C++
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:

C++
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:

C++
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:

C++
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:

C++
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:

C++
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:

C++
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:

C++
// 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:

C++
// 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)


Written By
Software Developer
Greece Greece
I'm working in C++, PHP , Java, Windows, iOS, Android and Web (HTML/Javascript/CSS).

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

My home page: https://www.turbo-play.com

Comments and Discussions

 
QuestionHello Pin
Member 1265561126-Jul-16 20:50
Member 1265561126-Jul-16 20:50 
Thank you for your great article. It is the clearest i have found and i am sure it is going to help me in my effort to learn. I love to read this blog and i look forward to reading more from you.
Regards
Dhiren
<a href="http://www.techstack.in/big-data-hadoop/">big data and hadoop training</a>
Generaluseful Pin
BillW3317-Feb-16 7:31
professionalBillW3317-Feb-16 7:31 
Questionhave you consider to post this as a tip? Pin
Nelek6-Feb-16 23:49
protectorNelek6-Feb-16 23:49 
Suggestionusing with references Pin
dron.plane2-Feb-16 9:12
dron.plane2-Feb-16 9:12 
GeneralRe: using with references Pin
Michael Chourdakis2-Feb-16 9:39
mvaMichael Chourdakis2-Feb-16 9:39 
GeneralRe: using with references Pin
dron.plane2-Feb-16 10:13
dron.plane2-Feb-16 10:13 
GeneralRe: using with references Pin
Michael Chourdakis3-Feb-16 6:25
mvaMichael Chourdakis3-Feb-16 6:25 
GeneralVery useful Pin
tkontos1-Feb-16 4:48
tkontos1-Feb-16 4:48 
QuestionUm... Pin
Jeremy Falcon30-Jan-16 7:33
professionalJeremy Falcon30-Jan-16 7: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.