I am trying to write a class template and internally it use a C
function (implementation of BFGS optimization, provided by the R
environment) with
Let me say up front:
I do not endorse the usage of the following library
#include
#include
#include
// func_traits
template
struct func_traits : public func_traits::operator())> {};
template
struct func_traits {
using ptr_type = Ret (*) (Args...);
using return_type = Ret;
template
struct arg
{
using type = typename std::tuple_element>::type;
};
template
using cast_return_type = Ret2 (*) (Args...);
};
template
struct func_traits : public func_traits {};
template
struct func_traits
{
using ptr_type = Ret (*) (Args...);
using return_type = Ret;
template
struct arg
{
using type = typename std::tuple_element>::type;
};
template
using cast_return_type = Ret2 (*) (Args...);
};
// constexpr counter
template
struct flag
{
friend constexpr int adl_flag(flag);
constexpr operator int() { return N; }
};
template
struct write
{
friend constexpr int adl_flag(flag) { return N; }
static constexpr int value = N;
};
template {})>
constexpr int read(int, flag, int R = read(0, flag{}))
{
return R;
}
template
constexpr int read(float, flag)
{
return N;
}
template
constexpr int counter(int R = write{})>::value)
{
return R;
}
// fnptr
template
class fnptr
{
//these are to make sure fnptr is never constructed
//technically the first one should be enough, but compilers are not entirely standard conformant
explicit fnptr() = delete;
fnptr(const fnptr&) {}
~fnptr() = delete;
template
static auto cast(Callable&& c, Ret(*fp)(Args...)) -> decltype(fp)
{
using callable_type = std::remove_reference_t;
static callable_type clb{std::forward(c)};
static bool full = false;
if(full)
{
clb.~callable_type();
new (&clb) decltype(clb){std::forward(c)};
}
else
full = true;
return [](Args... args) noexcept(noexcept(clb(std::forward(args)...))) -> Ret
{
return Ret(clb(std::forward(args)...));
};
}
public:
template
static Signature* cast(Callable&& c)
{
return cast(std::forward(c), static_cast(nullptr));
}
template
static auto cast(Ret (*fp)(Args...))
{
static decltype(fp) fnptr;
fnptr = fp;
using return_type = typename func_traits::return_type;
return [](Args... args) noexcept(noexcept(fp(std::forward(args)...)) -> return_type
{
return return_type(fnptr(std::forward(args)...));
};
}
template
static auto get(Callable&& c)
{
return cast(std::forward(c), typename func_traits::ptr_type{nullptr});
}
template
static auto get(Ret (*fp)(Args...))
{
return fp;
}
};
And use it as
#include
#include
using optimfn = double (int, double*, void*);
using optimgr = void (int, double*, double*, void*);
void test(optimfn* fn, optimgr* gr)
{
double d;
fn(42, &d, &d);
gr(42, &d, &d, &d);
}
int main()
{
std::function fn = [](int, double*, void*){
std::cout << "I'm fn" << std::endl;
return 0.;
};
std::function gr = [](int, double*, double*, void*){
std::cout << "I'm gr" << std::endl;
};
test(fnptr<>::get(fn), fnptr<>::get(gr));
}
Live example
func_traits
Is just a helper traits type that will fetch the type of any callable in an easily accessible form
constexpr counter
This is half the evilness of what's going on. For details visit is stateful metaprogramming ill formed yet?
fnptr
The actual meat of the code. It takes any callable with appropriate signatures and implicitly declares an anonymous C function at every point it is called and coerces the callable into the C function.
It has the funky syntax fnptr<>::get
and fnptr<>::cast
. This is intentional.
get
will declare the anonymous C function with the same signature as the callable object.
cast
works on any compatible callable type, that is, if the return type and arguments are implicitly convertible, it can be casted.
fnptr
implicitly declares an anonymous C function at each point in the code it is called. It is not the same as std::function
that is actually a variable.
If you call the same fnptr
in the code again, all hell breaks lose.
std::vector v;
for(int i = 0; i < 10; i++)
v.push_back(fnptr<>::get([i]{return i;})); // This will implode
You have been warned.