Skip to content
 

Calling function via void* pointer with arguments contained in std::container in a C++03 way

Recently I posted a technique that can be used to call functions, passed as raw void* pointers with arguments contained in an std::vector, so that the arguments are correctly transformed from container to normal C/C++–style arguments list. The method proposed was quite generalized and pretty type–safe. But it had one limitation: it required C++0x compiler supporting variadic templates.
So I decided to solve the original problem using C++03 compiler (which are mostly used in production today).

The main idea behind this method was that in case if compiler uses stack to pass parameters to called functions and also in case caller is responsible for stack maintenance then, essentially, calling a function with N parameters of type T sized X each is basically the same as calling this function with 1 “big” parameter sized N * X and which memory footprint is corresponding to passed parameters. This can be achieved, using just regular templates, but it has some limitations:

  • only arguments passed via stack pushing are supported;
  • caller maintains stack state, therefore target function can’t accept ellipsis (….) as an argument, e.g. printf can’t be called using this method;
  • target function has to accept N arguments of exactly same type, or if arguments are POD entities, exactly same size, otherwise — additional infrastructure has to be developed to provide correct type handling, which I hasn’t addressed (and am not planning to at the moment:)).

I think that’s all. Only working example is left to be published :)
We again used compile–time recursion to generate required a set of “big” types for us, for all possible cases (1 parameter, 2 parameters etc).

#include <cstdlib>
#include <vector>
#include <string>
#include <stdio.h>

// This is a "big" holder for function parameters
template <class TElement, int nElements>
struct ParamsHolder
{
    // Such layout ensures that copy constructors do get correctly called
    TElement m_Val[nElements];
};

template <class TElement, int nInst>
struct F
{
    typedef ParamsHolder<TElement, nInst> TParamsHolder;

    template <class TIt>
    static void f(void *pFunc, TIt itFirst, TIt itLast)
    {
        if (nInst == (itLast - itFirst))
        {
            // We're ready to call the function
            // Creating pseudo function signature
            void (*fun)(TParamsHolder) = reinterpret_cast<void(*)(TParamsHolder)>(pFunc);

            // Creating actual container for parameter values
            TParamsHolder paramsHolder;

            // Filling params holder from vector
            for (--itLast; itFirst <= itLast; --itLast)
                paramsHolder.m_Val[itLast - itFirst] = *itLast;

            // Calling the function
            fun(paramsHolder);
        }
        else
        {
            // We have to find right instantiation matching the number of passed parameters
            F<TElement, nInst + 1>::f(pFunc, itFirst, itLast);
        }
    }
};

template <class TElement>
struct F<TElement, 50>
{
    template <class TIt>
    static void f(void *pFunc, TIt itFirst, TIt itLast)
    {
        // runtime_assert(!"Please increase number of possible instantiations of F");
    }
};

template <class TElement, class TIt>
void call_fun(void *pFunc, TIt itFirst, TIt itLast)
{
    F<TElement, 0>::f(pFunc, itFirst, itLast);
}

// This is the target function that we'll pass as void*
void func(std::string p0, std::string p1)
{
    // parameters are correctly passed to here
}

int main(int argc, char** argv)
{
    // Container with parameters
    std::vector<std::string> strVec(2);

    strVec[0] = "Param 0!";
    strVec[1] = "Param 1!";

    // Getting raw function pointer
    void *pFunc = reinterpret_cast<void*>(&func);

    call_fun<std::string>(pFunc, strVec.begin(), strVec.end());

    return (EXIT_SUCCESS);
}

Leave a Reply