Template function signature unpacking in C++/CLI - c++

Is there a way to apply the function-signature-as-template-parameter unpacking idiom in a way that works with C++/CLI managed types?
As an example, consider the following code:
#include <msclr/gcroot.h>
using namespace System;
template<typename... Args>
ref struct ManagedDelegate abstract
{
delegate void Fn(Args...);
};
template<typename Signature>
struct Method;
template<typename... Args>
struct Method<void(Args...)>
{
using Fn = typename ManagedDelegate<Args...>::Fn;
Method(Fn^ m) : m_Method(m) {}
void operator()(Args... args)
{
auto method = safe_cast<Fn^>(m_Method);
method(args...);
}
private:
msclr::gcroot<Fn^> m_Method;
};
void f1(int a, int b)
{
Console::WriteLine("a = {0}, b = {1}", a, b);
}
void f2(String^ s)
{
Console::WriteLine("s = {0}", s);
}
int main(array<String ^> ^args)
{
using Method1 = Method<void(int, int)>;
Method1 m1(gcnew Method1::Fn(&f1));
m1(4, 5);
using Method2 = Method<void(String^)>;
Method2 m2(gcnew Method2::Fn(&f2));
m2("hello world");
return 0;
}
(The separate ManagedDelegate is a little annoying, but it's not possible to declare a delegate type inside a native class, sadly.)
If you comment out all the Method2 code at the bottom, then this compiles and runs as you'd expect -- it calls f1(4, 5) and prints accordingly.
Trying to do the same thing with a managed type argument, however, causes the template to fail to match the specialisation and results in:
error C2027: use of undefined type 'Method<void (System::String ^)>'
Is this a compiler bug, or is there some way to get this to work? There are some constraints that I do need to keep to in order for this to work in my real code:
Method needs to be an unmanaged type that contains a gcroot of the delegate type.
The use of templates rather than generics is intended. I don't think any of this is possible with generics anyway.
The non-use of std::forward is also intended, since this also upsets managed types. (And I'm not intending to pass native reference arguments anyway, so it's unnecessary.)
While I prefer automatically creating the delegate type from the signature as shown here, it would also be acceptable to create the delegate outside and pass it in instead of a signature, eg:
delegate void Method1Delegate(int, int);
...
Method<Method1Delegate> m1(gcnew Method1Delegate(&f1));
But either way, I do need an Args... parameter list (both for the operator() and for other reasons). And I don't think it's possible to extract this from a managed delegate type.
I also want the operator() to keep using Args... from the Method type so that it won't accept the "wrong" parameters. (I did have an older version of the code that templated Args directly on operator(), but this gives IntelliSense the false impression that it would accept any parameters.)
If there is a way to do the above, then I'd probably want a version that works with a templated return type as well as just void. I know how to do that with the above code -- just that any rewrite shouldn't prevent that working if possible.
EDIT: as demonstration that the managed args sort of work in variadics, this can be added:
template<>
struct Method<void(String^)>
{
using Fn = typename ManagedDelegate<String^>::Fn;
Method(Fn^ m) : m_Method(m) {}
template<typename... Args>
void operator()(Args... args)
{
auto method = safe_cast<Fn^>(m_Method);
method(args...);
}
private:
msclr::gcroot<Fn^> m_Method;
};
This works, provided that the call is changed to m2(gcnew String("hello world")); to force the correct type, or operator() is changed to accept a single String^ parameter instead of an open variadic. So the problem is definitely in matching a variadic template specialisation, not elsewhere.

I can mostly do what I want by abandoning the function-signature-specialisation and just specifying the signature components separately:
template<typename R, typename... Args>
ref struct ManagedDelegate abstract
{
delegate R Fn(Args...);
};
template<typename R, typename... Args>
struct Method
{
using Fn = typename ManagedDelegate<R, Args...>::Fn;
Method(Fn^ m) : m_Method(m) {}
R operator()(Args... args)
{
auto method = safe_cast<Fn^>(m_Method);
return method(args...);
}
private:
msclr::gcroot<Fn^> m_Method;
};
//...
using Method2 = Method<void, String^>;
Method2 m2(gcnew Method2::Fn(&f2));
m2("hello world");
This is not ideal, but it does compile and work. I'm still interested in any alternative answer that does support unpacking a function signature type, however. (And I filed the original issue as a compiler bug.)

Related

Expanding on a Job/Worker multi-threading system in C++

I've been reading a tutorial by Ben Hoffman (https://benhoffman.tech/cpp/general/2018/11/13/cpp-job-system.html)
I've had a go at bashing together a version of the Job/Worker system he has, but instead of using void* for arguments then casting to a known struct, I've been trying to use variadic arguments. The idea is, a job takes in a "parent" to perform a method on, the function pointer to said method, and an Args... for the argument(s). However, I get an internal compiler error if I try to build. Here is the job class:
template <class T, typename... Args>
struct JobMemberFunc : IJob
{
JobMemberFunc(T* aParent, void (T::* f)(Args...), Args... Args)
{
parentObj = aParent;
func_ptr = f;
saved_args = ::std::make_tuple (::std::move(Args)...);
}
virtual bool invoke() override
{
if (!parentObj) { return false; }
(parentObj->*func_ptr)(::std::move(saved_args));
return true;
}
/** the object to invoke the function pointer on */
T* parentObj;
/** The function pointer to call when we invoke this function */
void (T::* func_ptr)(Args...);
::std::tuple<Args...> saved_args;
};
struct CpuJob
{
IJob* jobPtr = nullptr;
};
Then there's the AddJob method, where the internal compiler error is actually happening.
template <typename T, typename... Args>
void AddJob(T* aParent, void(T::* func_ptr)(Args...), Args... args)
{//This curly bracket is where the internal compiler error happens
CpuJob aJob = {};
JobMemberFunc<T, Args...>* jobPtr = new JobMemberFunc<T, Args...>(aParent, func_ptr,
std::forward<Args>(args)...);
aJob.jobPtr = jobPtr;
locklessReadyQueue.enqueue(aJob);
}
More than happy to be told this is a bad/wrong way of trying to do it anyway. I have thought about doing away with it and having a standardized argument list or doing something polymorphic but I really wanna make this work so I can literally ask the job system to do anything I like.
Thanks!
std::function<void()> (in combination with lambdas) already do what you're trying to do with JobMemberFunc.
void AddJob(std::function<void()>&& job)
{
locklessReadyQueue.enqueue(std::move(job));
}
With this you can submit any function call as a job.
For example, a call some_obj.some_method(some_arg) becomes:
AddJob([&] { some_obj.some_method(some_arg); });
No more ugly pointer-to-member stuff...
You can find more complete thread pooling examples here: Thread pooling in C++11

Function pointer overloading using typedef

As I understand, typedef cannot be used for overloading but what if I need to use some different types as arguments to the function pointer?
How can I make it work with the following functionality?
{
public:
typedef void (*InitFunc)(float x);
typedef void (*InitFunc)(int a, char b); //Needs to be added
virtual void initialize(InitFunc init) = 0;
};
Edit:
I cannot use C++17, so can't use variant
As commented, the easiest way is a union, although not very type safe and C++-y. Here is an example with inheritance, since you commented that you want inheritance.
typedef void (*FloatInit)(float x);
typedef void (*IntCharInit)(int a, char b);
union InitFn {
FloatInit fi;
IntCharInit ici;
};
struct Foo {
void initialize(InitFn) = 0;
};
struct FloatFoo: public Foo {
void initialize(InitFn f) override {
f.fi(42.0f);
}
};
void test(float) {}
// ...
auto x = FloatFoo{};
x.initialize(InitFn{test});
As mentioned by other commenters, you can use std::variant to enhance type safety and get rid of the manual union definition:
typedef void (*FloatInit)(float x);
typedef void (*IntCharInit)(int a, char b);
typedef std::variant<FloatInit, IntCharInit> InitFn;
struct Foo {
void initialize(InitFn) = 0;
};
struct FloatFoo: public Foo {
void initialize(InitFn f) override {
std::get<FloatInit>(f)(42.0f);
}
};
void test(float) {}
// ...
auto x = FloatFoo{};
x.initialize(InitFn{test});
One solution is to create a simple wrapper class template instead, to allow the compiler to automatically generate instantiations as necessary. This is relatively simple if init is always guaranteed to be a non-member function (and by extension, an actual function and not a functor/lambda).
// Quick-and-dirty transparent callable wrapper, to serve as overloadable "type alias".
template<typename>
class InitFunc;
template<typename Ret, typename... Params>
class InitFunc<Ret(*)(Params...)> {
public:
// Supply component types if needed.
// Tuple used for params, for convenience.
using return_type = Ret;
using param_types = std::tuple<Params...>;
using func_type = Ret(Params...);
using func_ptr_type = func_type*;
using func_ref_type = func_type&;
// Create from pointer or reference.
constexpr InitFunc(func_ptr_type p = nullptr) : ptr(p) {}
constexpr InitFunc(func_ref_type r) : ptr(&r) {}
// Transparent invocation.
// Deduces argument types instead of relying on Params, to allow for perfect forwarding.
template<typename... Ts>
constexpr return_type operator()(Ts&&... ts) { return ptr(std::forward<Ts>(ts)...); }
// Convert back to original type if necessary.
operator func_ptr_type() { return ptr; }
operator func_ref_type() { return *ptr; }
private:
// Actual function pointer.
func_ptr_type ptr;
};
// And a nice, clean creator, which can be renamed as necessary.
template<typename Init>
constexpr auto make(Init func) { return InitFunc<Init>(func); }
This creates a nice little wrapper that can easily be optimised out entirely, and will compile as long as C++14 support is available.
Note that you require a C++11 compiler (or variadic templates, rvalue references, perfect forwarding, and constexpr support) at the absolute minimum, and will need to modify make() to have a trailing return type for pre-C++14 compilers. I believe this is compatible with C++11 constexpr, but I'm not 100% sure.
If you want InitFunc to be able to accept pointers/references-to-member-function (including functors and lambdas), you'll need to provide an additional version to isolate it into a non-member "function", and likely bind it to a class instance. It may be worth looking into std::bind() in this case, although I'm not sure if it has any overhead.
In this case, I would suggest splitting the member types off into a base class, to reduce the amount of code you'll need to duplicate.
// Quick-and-dirty transparent callable wrapper, to serve as overloadable "type alias".
template<typename>
class InitFunc;
// Supply component types if needed.
// Tuple used for params, for convenience.
// Using actual function type as a base, similar to std::function.
template<typename Ret, typename... Params>
class InitFunc<Ret(Params...)> {
public:
using return_type = Ret;
using param_types = std::tuple<Params...>;
using func_type = Ret(Params...);
using func_ptr_type = func_type*;
using func_ref_type = func_type&;
};
// Non-member functions.
// As member types are now dependent types, we qualify them and use `typename`.
// Yes, it looks just as silly as you think it does.
template<typename Ret, typename... Params>
class InitFunc<Ret(*)(Params...)> : public InitFunc<Ret(Params...)> {
// Actual function pointer.
typename InitFunc::func_ptr_type ptr;
public:
// Create from pointer or reference.
constexpr InitFunc(typename InitFunc::func_ptr_type p = nullptr) : ptr(p) {}
constexpr InitFunc(typename InitFunc::func_ref_type r) : ptr(&r) {}
// Transparent invocation.
// Deduces argument types instead of relying on Params, to allow for perfect forwarding.
template<typename... Ts>
constexpr typename InitFunc::return_type operator()(Ts&&... ts) { return ptr(std::forward<Ts>(ts)...); }
// Convert back to original type if necessary.
operator typename InitFunc::func_ptr_type() { return ptr; }
operator typename InitFunc::func_ref_type() { return *ptr; }
};
// See ecatmur's http://stackoverflow.com/a/13359520/5386374 for how to accomodate member functions.
// ...
// Non-member function make() is unaffected.
// An overload will likely be needed for member functions.
template<typename Init>
auto make(Init func) { return InitFunc<Init>(func); }
Despite the awkwardness inside our derived specialisation, any code that relies on InitFunc shouldn't (to my knowledge) see any changes to its API; the previous example will work just fine if we swap to this new InitFunc, and be none the wiser after recompilation.
Note that it will change the ABI, though, and thus any code compiled for the simpler InitFunc will need to be recompiled for this version.

Hiding variadic template implementation

I have some 3rdParty library with a method like this:
bool Invoke(const char* method, Value* args, size_t nargs)
It takes an array of its inner type (convertible to any primitive c++ types) and arg count as its inner params.
In my code, I wrote some generalized helper to avoid manual creation and type convertion for each invoke:
template<class ... Args>
bool WrappedValue::Invoke(const char* method, Args&& ... args)
{
3rdParty::Value values[] =
{
3rdParty::Value(std::forward<Args>(args))...
}
return m_value.Invoke(method, values, sizeof ... (Args));
}
It works just fine, but now I should have 3rdParty code defined in my header files and lib connected directly to my main project.
Is it possible to hide implementation details and usage of this 3rd party library? (Use some kind of pimple idiom or proxy object for 3rdParty::Value ). I know that it's not possible to use virtual template methods in c++ to create a proxy or simply move template implementation to .cpp, so I am totally stuck with this problem.
Will be grateful for any help)
Sure. Simply write the equivalent of std::variant<int, double, char, every, other, primitive, type>.
Now your Invoke converts your args into an array (vector, span, whatever) of those variants.
Then you pass this array of variants to your internal Invoke method.
That internal invoke method then uses the equivalent of std::visit to generate a 3rdParty::Value from each of your variants.
Boost provides a boost::variant which would probably work.
You could also roll this by hand. By narrowly specifying your problem, you'd get away with something simpler than a std::variant. It would be more than a bit of work, however.
Another approach is this
template<class T> struct tag_t {constexpr tag_t(){}; using type=T;};
template<class T> constexpr tag_t<T> tag{};
template<class T, class F, class ... Args>
bool WrappedValue::Invoke(tag_t<T>, F&& f, const char* method, Args&& ... args)
{
T values[] = {
T(std::forward<Args>(args))...
};
return std::forward<F>(f)(method, values, sizeof...(Args));
}
which is simpler. Here you'd write:
bool r = Invoke( tag<3rdParty::Value>, [&](const char* method, 3rdParty::Value* values, std::size_t count) {
m_value.Invoke( method, values, count );
}, 3.14, 42, "hello world");
If you want to avoid exposing the 3rdParty API, you need some non-template method to pass the data. That inevitably would require some type-erasure mechanism (like std::any), which instead is exposed in your API.
So, yes you could do that, but then the 3rdParty Value is already a type erasure method and this would only pass the data from one type erasure to the next, creating additional overhead. Whether that price is worth paying only you can decide.
I somehow overlooked your remark that the arguments are all primitive. In this case, type erasure is much simpler and can be done via a tag+union like
struct erasure_of_primitive
{
enum { is_void=0, is_str=1, is_int=2, is_flt=3, is_ptr=4 }
int type = is_void;
union {
const char*s; // pointer to external C-string
int64_t i; // any integer
double d; // any floating point number
void*p; // any pointer
} u;
erasure_of_primitive() = default;
erasure_of_primitive(erasure_of_primitive&const) = default;
erasure_of_primitive&operator=(erasure_of_primitive&const) = default;
erasure_of_primitive(const char*str)
: type(is_str), u.s(str) {}
template<typename T>
erasure_of_primitive(T x, enable_if_t<is_integer<T>::value>* =0)
: type(is_int), u.i(x) {}
template<typename T>
erasure_of_primitive(T x, enable_if_t<is_floating_point<T>::value>* =0)
: type(is_flt), u.d(x) {}
template<typename T>
erasure_of_primitive(T*x)
: type(is_ptr), u.p(static_cast<void*>(x)) {}
};

C++ templated callback bounce function

I've been trying to come up with a templated function that generalizes the bounce procedure when dealing with C APIs that use function pointer callbacks.
I've mostly figured it out and have a working system, but I'm wondering if there is a way to clean up the final step.
Imagine you have an API that takes a function pointer and a user data pointer. You want to use an instance method as the callback target. This requires a "bounce" function that reinterprets the user data pointer as an instance pointer and calls the method with the rest of the arguments.
The following example code works:
#include <cstdio>
class Foo {
public:
Foo(int val) : val_(val) { }
void baz(int v) const
{
printf("baz %d\n", v + val_);
}
private:
int val_;
};
// Templated bounce function
template<class T, class Method, Method m, class Ret, class ...Args>
static Ret bounce(void *priv, Args... args)
{
return ((*reinterpret_cast<T *>(priv)).*m)(args...);
}
#define BOUNCE(c, m) bounce<c, decltype(&c::m), &c::m>
// Callback simulator
void call_callback(void (*func)(void *, int), void *priv, int v)
{
if (func) {
func(priv, v);
}
}
// Main Entry
int main()
{
Foo bar(13);
call_callback(&bounce<Foo, decltype(&Foo::baz), &Foo::baz>, &bar, 10);
call_callback(&BOUNCE(Foo, baz), &bar, 11);
return 0;
}
Basically I'm looking for a way to clean up the usage. The macro works but I'm trying to instead find some type of helper function that can just take a method pointer parameter like &Foo::baz and deduce all the parameters. Something like a bounce_gen(&Foo::baz) that would return a pointer to the actual bounce function.
It has been a fun exercise, but I can't quite get the last piece.
The type of a member function pointer contains the class type and the function signature. So, you can let template function argument deduction handle this for you:
template<class T, class Method, class ...Args>
static auto bounce(Method T::*func, T* priv, Args... args) -> decltype((priv->*m)(args...))
{
return (priv->*m)(args...);
}
More convenient might be to either use std::bind or a lambda to completely hide the fact that it is a member function call:
template<class Func, class ...Args>
static auto bounceCallable(Func func, Args... args) -> decltype(func(args...))
{
return func(args...);
}
And you would call it like this:
call_callback([&bar](int v){bar.baz(v);}, 11);
With a lambda, you have a syntax nicer than with std::bind, but it comes at the cost of having to repeat the signature.

Pass overloaded member function to a variadic template function

I have a class with a function add:
class Pool {
public:
Pool() {};
template<class F, class... A>
auto add(F&& f, A&&... args) -> std::future<typename std::result_of<F(A...)>::type>
{
// return empty placeholder, for the sake of this code example
std::future<typename std::result_of<F(A...)>::type> ret;
return ret;
};
};
It should take any function with its arguments, add it to a thread pool and return a future of the result type of that function.
And a class where I use it:
class MyClass {
public:
string doIt(string) { return string("a"); };
string doIt(int, string) { return string("b"); };
void test() {
Pool myPool;
string a("test");
myPool.add(&MyClass::doIt, a); // Error
};
};
Which gives a compiler error:
Error 1 error C2914: 'Pool::add' : cannot deduce template argument as function argument is ambiguous MyClass.cpp 94
Now the problem is (I think) that the compiler can't deduce which overload I want to use. Similar to Overloaded function as argument of variadic template function.
(Also I'm not 100% clear on why I have to use "&" for class member functions, but no ampersand if I pass in a free function).
Anyway I also tried the workaround mentioned in above answer:
struct doIt_wrapper {
template <typename... T>
auto operator()(T... args) -> decltype(doIt(args...)) {
return doIt(args...);
}
};
and then modifying MyClass::test() to:
void test() {
Pool myPool;
string a("test");
myPool.add(doIt_wrapper(), a);
};
But it also gives me a compiler error:
error C2893: Failed to specialize function template 'unknown-type doIt_wrapper::operator ()(T...)' C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xrefwrap 58
I also tried a few variants like myPool.add(doIt_wrapper<string>() and with/without '&' but they all generate one or the other compiler error.
I think I don't fully understand the problem yet and I would be glad if someone could shed light on it. Also I am looking for a proper solution to this problem. It can't really be the case that this only works as long as there are no two functions with the same name, and as soon as there are, everything breaks down without a proper, generic solution?
Edit: Fixed a few typos and uploaded a minimal example here: http://ideone.com/eX1r1l
As others have mentioned, the problem is that doIt() is not callable inside the doIt_wrapper class as it also needs a pointer to the object called on.
You could just modify the doIt_wrapper operator() to also take a pointer to the object and pass a pointer to this as first argument to add().
It would then look something like this:
#include <iostream>
#include <future>
using namespace std;
class Pool {
public:
Pool() {};
template<class F, class... A>
auto add(F&& f, A&&... args) -> std::future<typename std::result_of<F&&(A&&...)>::type>
{
// return empty placeholder, for the sake of this code example
std::future<typename std::result_of<F&&(A&&...)>::type> ret;
return ret;
};
};
class MyClass {
public:
string doIt(string) { return string("a"); };
string doIt(int, string) { return string("b"); };
struct doIt_wrapper
{
template<class T, class... Ts>
auto operator()(T&& t, Ts&&... args) -> decltype(t->doIt(std::forward<Ts>(args)...))
{
return t->doIt(std::forward<Ts>(args)...);
}
};
void test() {
Pool myPool;
string a("test");
myPool.add(doIt_wrapper(), this, a); // No error no more
};
};
int main() {
// your code goes here
MyClass my;
my.test();
return 0;
}
This way you don't have to do the casts. The code compiles on both GCC and Clang.
You may use lambda:
myPool.add([this](const std::string& s) {doIt(s);}, a);
or even
myPool.add([this, a]() {doIt(a);});
Currently, you may indicate which overload to use that way:
myPool.add(static_cast<std::string (MyClass::*) (std::string)>(&MyClass::doIt), a);
Note that doIt is a method (not a free function or static function), so you have to call it with an object.
If you add static to doIt, you may choose the overload with
myPool.add(static_cast<std::string (*) (std::string)>(&MyClass::doIt), a);
The problem is that non-static member functions have a hidden, implicit this parameter that points to the instance that called it. Your compiler is right to reject it as it doesn't have the correct arguments for the function. Sending in this as an additional bound parameter will work:
myPool.add(&MyClass::doIt, this, a);
// ^^^^
Using a lamda expression will work as well.
Also, the standard library function std::async() already does what you're trying to do here, and more. Consider using that instead.
Edit: You also need to cast to the correct type to select the correct overload. #Jarod42 already shows you how.