Specialising templates on C++AMP restricted lambdas - visual-c++

Using the insight of this question (and a few others) I have been able to write the following for interrogating normal lambda function type infromation (i.e. return type, argument count etc)
// helper classes ========================================
template <typename R, typename... A>
class lambda_traits_evaluation {
public:
typedef R r_type;
enum { n_args = sizeof...(A) };
// ...
};
template <typename R, typename... A>
class lambda_traits_helper
: public lambda_traits_evaluation<R,A...>{};
template <typename R, typename F, typename... A>
class lambda_traits_helper<R (F::*)(A...) const>
: public lambda_traits_evaluation<R,A...>{};
// use class ========================================
template <typename F>
class lambda_traits {
typedef typename lambda_traits_helper<decltype(&F::operator())> helper_impl;
// ...
}
I can then use this with lambda_traits<decltype(myLambda)> but that is where my smug coding ends because if my lambda is amp restricted for the gpu i.e.
auto myLambda = [](int) restrict(amp) -> void {};
as obviously the template specialisation is not picked up. However adding the new specialisation
template <typename R, typename F, typename... A>
class lambda_traits_helper<R (F::*)(A...) const restrict(amp)>
: public lambda_traits_evaluation<R,A...> {};
still does not solve the problem as I discover that the compiler barks
error C3939: 'abstract declarator' : pointer to member functions, function
pointers, references to functions with 'amp' restriction
specifier are not allowed
is there another way to interrogate the types in lambdas or else a way to strip the restrict off the lambda type?

The inability to form a pointer to an amp-restricted function, even in unevaluated context, is a bummer. There is however a workaround, which is viable as long as you can require the amp-restricted lambdas to be cpu,amp-restricted. In such case you can cast-away the amp-restriction, forming a pointer to the cpu-restricted member function -- which you can interrogate further.
See the following proof-of-concept:
#include <type_traits>
template <typename R, typename F, typename... A>
auto get_R(R (F::*)(A...) const) -> R
{}
template <typename L>
struct lambda_traits
{
using ret_type = decltype(get_R(&L::operator()));
};
int main()
{
auto lambda_1 = [](int) restrict(cpu,amp) -> void {};
auto lambda_2 = [](int) restrict(cpu,amp) -> int { return 0; };
// Test:
static_assert(std::is_same<lambda_traits<decltype(lambda_1)>::ret_type, void>::value, "Failed 1.");
static_assert(std::is_same<lambda_traits<decltype(lambda_2)>::ret_type, int>::value, "Failed 2.");
}
Hope that helps!

Related

function template specialization for variadic templated function (printf)

I have some Arduino C++11 code that I'm trying to improve: trying to make a printf-like function treat String specially so I don't have to call c_str() myself everywhere I use it. Basically for any builtin type like int float bool etc, I just want to pass the arg as-is, and for String, pass return the c_str(). Hit some snags so I tried this in some of the online compilers available. Starting point is this, using std::string instead of String:
#include <string>
class SerialOut {
public:
template<class ...Ts>
static void error(const char* msg, Ts... args) {
printf(msg, args...);
}
};
int main() {
std::string greeting("hi");
SerialOut::error("Message %d %s\n", 1, greeting.c_str());
}
So I tried creating a function template that just returns the value it gets, with a specialization for std::string:
#include <string>
template <typename T, typename R=T> R raw(T& x) {return x;}
template <> const char* raw<>(std::string& x) {return x.c_str();}
class SerialOut {
public:
template<class ...Ts>
static void error(const char* msg, Ts... args) {
printf(msg, raw(args)...);
}
};
int main() {
std::string greeting("hi");
SerialOut::error("Message %d %s\n", 1, greeting);
}
I get a compilation error when I run this in https://repl.it/languages/cpp11:
clang version 7.0.0-3~ubuntu0.18.04.1 (tags/RELEASE_700/final)
 clang++-7 -pthread -std=c++11 -o main main.cpp
main.cpp:10:25: error: cannot pass object of non-trivial type
'std::__cxx11::basic_string<char>' through variadic function; call will abort at
runtime [-Wnon-pod-varargs]
printf(msg, raw(args)...);
^
main.cpp:16:20: note: in instantiation of function template specialization
'SerialOut::error<int, std::__cxx11::basic_string<char> >' requested here
SerialOut::error("Message %d %s\n", 1, greeting);
^
1 error generated.
compiler exit status 1
With https://www.onlinegdb.com/online_c++_compiler there is no error but the raw() specialization is not selected, so the output for greeting is garbage.
In Arduino IDE I get a slightly different error (after replacing std::string by String, of course):
sketch\mqtt.cpp.o: In function `char const* raw<String, char const*>(String&)':
sketch/utils.h:15: multiple definition of `char const* raw<String, char const*>(String&)'
sketch\Thermistor.cpp.o:sketch/utils.h:15: first defined here
sketch\sketch.ino.cpp.o: In function `char const* raw<String, char const*>(String&)':
sketch/utils.h:15: multiple definition of `char const* raw<String, char const*>(String&)'
sketch\Thermistor.cpp.o:sketch/utils.h:15: first defined here
I tried several variations on the raw() functions, to no avail. I figure I'm just missing a subtlety or it's just not possible to do this in C++11.
Update: I found Variadic Macro: cannot pass objects of non-trivially-copyable type through '...', one of the answers solves the above in C++14 (basically use decltype(auto) and overload instead of specialization). I added a slight variation on it that works also in C++11, and with "inline" it also works in Arduino C++ (without "inline" on the overload, the above message about multiple definitions -- turns out this is a linker message so it does compile, I guess the Arduino variant doesn't inline "obviously inlined" functions as other compilers).
Something along these lines, perhaps:
template <typename T>
struct SerialHelper {
static T raw(T val) { return val; }
};
template <>
struct SerialHelper<std::string> {
static const char* raw(const std::string& val) { return val.c_str(); }
};
class SerialOut {
public:
template<class ...Ts>
static void error(const char* msg, Ts... args) {
printf(msg, SerialHelper<Ts>::raw(args)...);
}
};
Demo
Based on Variadic Macro: cannot pass objects of non-trivially-copyable type through '...' I got it to work with this very simple change, which works in C++11 and Arduino C++:
#include <string>
template <typename T> T raw(const T& x) {return x;}
inline const char* raw(const String& x) {return x.c_str();}
class SerialOut {
public:
template<class ...Ts>
static void error(const char* msg, Ts... args) {
printf(msg, raw(args)...);
}
};
int main() {
std::string greeting("hi");
SerialOut::error("Message %d %s\n", 1, greeting);
}
Thanks to #IgorTandetnik comment, it is clear why.

Converting all variadic arguments into a single std::string via move semantics

I have a class that contains a lambda function that looks like this:
class Foo {
private:
inline static std::string text{};
public:
template<typename...T>
inline static auto func = [](T&&...args) mutable throw() {
text += std::string(args...);
};
void show() {
std::cout << text << '\n';
}
};
My intended use would be something like this:
int main() {
Foo bar;
bar.func<std::string, int, std::string>( "Hello, I am", 39, "years old!");
bar.show();
return 0;
}
I want the templated variadic lambda to take in any type of parameter that is a basic type such as string, char*, char[], int, float, double, etc... and to convert all of them into a single std::string that will be stored within the class.
When I run my code as such:
int main() {
Foo bar;
bar.func<string>( "Hello world!");
bar.show();
return 0;
}
Everything compiles fine, however, when I begin to add in various types such as the example from the intended use above, it fails to compile. Microsoft Visual Studio is giving me a C2400 compiler error: cannot convert from initialize list to std::string. No constructor could take the source type, or constructor overload resolution was ambiguous...
I believe I understand why it is ambiguous as that's not so much the issue. My question is what would be the proper and efficient way of using "move semantics or perfect forwarding"? I'm trying to avoid a bunch of copies of temporaries.
You could use fold expressions:
template<typename...T>
inline static auto func = [](T&&...args) mutable throw() {
text += (toString(args) + ...);
};
where toString is defined as:
template<class T>
std::string toString(T&& t){
if constexpr (std::is_arithmetic_v<std::decay_t<T>>)
return std::to_string(std::forward<T>(t));
else
return std::forward<T>(t);
}
you can extend toString to handle all types you need to convert to string.
Demo

Problem with Formatting User-defined Types with fmt library

I faced a problem for formatting a user-defined type and ended up with this simple example based on fmt documentation.
struct point_double {
double x, y;
operator const char*() const {
return nullptr;
}
};
namespace fmt {
template <>
struct formatter<point_double> {
template <typename ParseContext>
constexpr auto parse(ParseContext& ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const point_double& p, FormatContext& ctx) {
return format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y);
}
};
} // namespace fmt
void foo() {
point_double p = {1, 2};
fmt::print("{}\n", p);
}
Calling foo will crash because the user-defined formatter is not used. Instead fmt::print will use the default string formatter and crash as the operator returns nullptr.
Is there a way to go around this issue? I'm using fmt 5.3.0
You cannot have both an implicit conversion to const char* and a formatter specialization ({fmt} will now give you a compile-time error), because const char* is already formattable. If you have control over point_double, an easy solution is to make the conversion operator explicit which is a good idea in general. Otherwise you can wrap point_double in another type and provide a formatter specialization for that.

C++11 thread wrapper function

I'd like to have a wrapper thread function, i.e. a function executed by a thread which does some extra stuff, and then calls the user function.
template<class F, class... Args>
void wrapper(F&& user_function, Args&&... args) {
// do some extra stuff
user_function(args); // maybe I need to forward args
// do some extra stuff
}
Ok, this could be a nice wrapper, so I need a manager that uses this wrapper function and allows the user to spawn his own threads:
class ThreadManager {
public:
template<class F, class... Args>
std::thread newThread(F&& f, Args&&... args) {
return std::thread(thread_wrapper<F,Args...>, std::forward<F>(f), std::forward<Args>(args)...);
}
};
this way the thread manager SHOULD spawn a thread that uses the wrapper function which, in turn, does its extra work and calls the user function.
But the compiler now says: Attempt to use a deleted function.
The error is in the thread header:
template <class _Fp, class ..._Args, size_t ..._Indices>
inline _LIBCPP_INLINE_VISIBILITY
void
__thread_execute(tuple<_Fp, _Args...>& __t, __tuple_indices<_Indices...>)
{
__invoke(_VSTD::move(_VSTD::get<0>(__t)), _VSTD::move(_VSTD::get<_Indices>(__t))...);
}
What am I missing/doing wrong?
[edit]
Using test:
void foo(int i) {
std::cout << "foo: " << i << std::endl;
}
int main(int argc, const char *argv[]) {
ThreadManager mgr;
auto t = mgr.newThread(foo, 10);
t.detach();
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
return 0;
}
I'm using Xcode 7.1 with LLVM compiler, but fails on FreeBSD clang 3.3 too.
The Xcode error is:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/thread:337:5: error: attempt to use a deleted function
__invoke(_VSTD::move(_VSTD::get<0>(__t)), _VSTD::move(_VSTD::get<_Indices>(__t))...);
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/thread:347:5: note: in instantiation of function template specialization 'std::__1::__thread_execute' requested here
__thread_execute(*__p, _Index());
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/thread:359:42: note: in instantiation of function template specialization 'std::__1::__thread_proxy >' requested here
int __ec = pthread_create(&__t_, 0, &__thread_proxy<_Gp>, __p.get());
I'm not sure what is causing the "Attempt to use a deleted function" in your example, I get other errors related to std::thread's bind mechanism.
It appears the way you are spelling out the template arguments for thread_wrapper is not playing nice with std::thread's constructor - in particular when it uses a simplified std::bind internally. The mix of perfectly forwarded function types and std::decayed function pointers seems to upset std::result_of.
we can make it work by applying some std::decay in newThread ourselves:
return std::thread( thread_wrapper<typename std::decay<F>::type,
typename std::decay<Args>::type...>,
std::forward<F>(f),
std::forward<Args>(args)... );
...but to be honest I'm not entirely sure why that works.
Alternatively, with some indirection and more forwarding, we can avoid having to spell out the template arguments.
We just need a functor that forwards to thread_wrapper (or a polymorphic lambda in C++14):
struct wrapper_helper {
template<class F, class... Args>
void operator()(F&& f, Args&&... args) const {
thread_wrapper(std::forward<F>(f), std::forward<Args>(args)...);
}
};
And use it in newThread:
return std::thread(wrapper_helper{}, std::forward<F>(f), std::forward<Args>(args)...);
Here's the full example showing arguments passed by value, reference and rvalue reference working as intended: http://coliru.stacked-crooked.com/a/b75d5a264f583237
Note: For move-only types like std::unique_ptr, you will definitely want to forward args... in thread_wrapper.

can't pass std::vector<std::unique_ptr<>> to std::thread

I created a threadpool which captures a function and arguments into tuples and then perfect forwards when the task is dequeued.
However I am unable to pass a vector of unique_ptr's to the thread by rvalue. A simplified project is below:
#include <future>
#include <memory>
#include <vector>
template <typename F, typename... Args>
typename std::result_of<F(Args...)>::type pushTask(F&& f, Args&&... args)
{
using result_type = typename std::result_of<F(Args...)>::type;
// create a functional object of the passed function with the signature std::function<result_type(void)> by creating a
// bound Functor lambda which will bind the arguments to the function call through perfect forwarding and lambda capture
auto boundFunctor = [func = std::move(std::forward<F>(f)),
argsTuple = std::move(std::make_tuple(std::forward<Args>(args)...))](void) mutable->result_type
{
// forward function and turn variadic arguments into a tuple
return result_type();
};
// create a packaged task of the function object
std::packaged_task<result_type(void)> taskFunctor{ std::move(boundFunctor) };
}
int main(int argc, char *argv [])
{
auto testvup = [](std::vector<std::unique_ptr<int>>&& vup)
{
};
std::vector<std::unique_ptr<int>> vup;
pushTask(testvup, std::move(vup));
}
I get the following compiler error with VS2015 rather I use the std::function or std::packaged_task
Severity Description Project File Line
Error error C2280: 'std::unique_ptr<int,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': attempting to reference a deleted function Stack xmemory0 659
passing other arguments by rvalue including std::vector works.
Has anyone else run across this or have suggestions.
C++ Standard section §20.9.11.2.1 [func.wrap.func]
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F
f);
Requires: F shall be CopyConstructible. f shall be Callable for
argument types ArgTypes and return type R. The copy constructor and
destructor of A shall not throw exceptions.
Your lambda function boundFunctor is a move only type (because it captures move only types, since std::unique_ptr cannot be copied)
Hence, boundFunctor is not copyable and not suitable as an argument to an std::function

Resources