-
Notifications
You must be signed in to change notification settings - Fork 0
In Depth
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.
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.
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.