Skip to content

In Depth

Klemens Morgenstern edited this page Mar 23, 2018 · 2 revisions

Constructed Wraps

The macros without the _FIX appendix are runtime-wraps. What this means is that they are members of a class and are constructed with it. The macros are:

  • CPP_WRAP_FN
  • CPP_WRAP_MEM
  • CPP_WRAP_STATIC_MEM

So let's consider an example, with two functions:

//foo.cpp
int foo() {return 0;}
//multiple_stubs.cpp
#include <cassert>
#include <cpp/wrap.hpp>

struct stubber1
{
    CPP_WRAP_FN(foo, int, ())
    {
        return 42;
    }
};

struct stubber2
{
    CPP_WRAP_FN(foo, int, ())
    {
        return -1;
    }
};

int main(int argc, char * argv[])
{
    assert(foo() == 0);
    try
    {
        stubber1 s; /*<Connect the first stub.>*/
        assert(foo() == 42); /*<Check that the stub works>*/
        stubber2 s2; /*<This throws an exception, becaues it is already connects>*/
    }
    catch(std::runtime_error&) {}

    stubber2 s; /*<Since the first one is destructed we now can use the other one>*/
    assert(foo() == -1); /*<Check if it works>*/
    return 0;
}

The example given above will create stubs at runtime so that several can be used in the same binary. Additionally the original function can be used. This also means that a link to the original function will be created, so there's a guarantee that the symbol-to-wrap is actually defined.

Trying to construct several objects with 'CPP_WRAP_FN' will cause an exception.

Fixed Wraps

The fix wraps have a post-fix _FIX wrap using global functions at compile-time.

//fixed_stub.cpp
#include <cassert>
#include <cpp/wrap.hpp>

CPP_WRAP_FN_FIX(foo, int, ())
{
    return 42;
}

int main(int argc, char * argv[])
{
    assert(foo() == 42);
    return 0;
}

The disadvantage here is that the original function cannot be used (as in is not referenced) and thus there will be no link be generated against it. This means that the linker will not generate an error if the wrapped function is not present.

Using Templates

C++11 introduced the extern template which will generate extern linkage for templates. So let's do this in an example, we'll use 'std::vector' for this. We'll put the template instanciation into a source fill, which needs to be compiled and linked.

#include <vector>
namespace std
{
template class std::vector<int, std::allocator<int>>; //define it
}

In our test we tell the compiler that vector will be externally linked and declare the wraps:

//template_stub.cpp
#include <vector>
#include <cassert>
#include <cpp/wrap.hpp>

namespace std
{
extern template class std::vector<int, std::allocator<int>>; //declare it
}

struct stubber
{
    std::size_t size_out;
    int push_back_in;
    CPP_WRAP_MEM(const std::vector<int>, size, std::size_t, ()) /*<Declaring it const is applied to the method qualifier>*/
    {
        return size_out;
    }
    CPP_WRAP_MEM(std::vector<int>, push_back, void, (int&& in)) /*<only stub move push_back>*/
    {
        size_out = in;
    }
};

int main(int argc, char * argv[])
{
    std::vector<int> vec = {1,2,3};
    assert(vec.size() == 3);

    stubber s;
    s.size_out = 42;
    assert(vec.size() == 42);
    vec.push_back(3); /*<This will invoke the move-push_back>*/
    assert(s.push_back_in == 3);

    return 0;
}

By utilizing extern template we can thus wrap template functions or member functions of template classes.

The class-name can be put into parenthesis if it is a template containing a comma.