Why doesn't copy elision work in my static functional implementation? - c++

I am trying to implement a "static" sized function, that uses preallocated store, unlike std::function that uses dynamic heap allocations.
#include <utility>
#include <cstddef>
#include <type_traits>
template <typename T, size_t StackSize = 64>
class static_function;
// TODO: move and swap
// - can move smaller instance to larger instance
// - only instances of the same size are swappable
// TODO: condiotnal dynamic storage?
template <typename Ret, typename ... Args, size_t StackSize>
class static_function<Ret(Args...), StackSize>
{
public:
constexpr static size_t static_size = StackSize;
using return_type = Ret;
template <typename Callable>
constexpr explicit static_function(Callable &&callable)
: pVTable_(std::addressof(v_table::template get<Callable>()))
{
static_assert(sizeof(std::decay_t<Callable>) <= static_size, "Callable type is too big!");
new (&data_) std::decay_t<Callable>(std::forward<Callable>(callable));
}
constexpr return_type operator()(Args ... args) const
{
return (*pVTable_)(data_, std::move(args)...);
}
~static_function() noexcept
{
pVTable_->destroy(data_);
}
private:
using stack_data = std::aligned_storage_t<static_size>;
struct v_table
{
virtual return_type operator()(const stack_data&, Args &&...) const = 0;
virtual void destroy(const stack_data&) const = 0;
template <typename Callable>
static const v_table& get()
{
struct : v_table {
return_type operator()(const stack_data &data, Args &&... args) const override
{
return (*reinterpret_cast<const Callable*>(&data))(std::move(args)...);
}
void destroy(const stack_data &data) const override
{
reinterpret_cast<const Callable*>(&data)->~Callable();
}
} constexpr static vTable_{};
return vTable_;
}
};
private:
stack_data data_;
const v_table *pVTable_;
};
However, it does not work as expected, since copies of the callable (copy elision does not kick in like in just lambda).
Here is what is expected vs actiual behavior with -O3:
#include <iostream>
#include <string>
int main()
{
struct prisoner
{
std::string name;
~prisoner()
{
if (!name.empty())
std::cout << name << " has been executed\n";
}
};
std::cout << "Expected:\n";
{
const auto &func = [captured = prisoner{"Pvt Ryan"}](int a, int b) -> std::string {
std::cout << captured.name << " has been captured!\n";
return std::string() + "oceanic " + std::to_string(a + b);
};
std::cout << func(4, 811) << '\n';
}
std::cout << "THE END\n\n";
std::cout << "Actual:\n";
{
const auto &func = static_function<std::string(int, int)>([captured = prisoner{"Pvt Ryan"}](int a, int b) -> std::string {
std::cout << captured.name << " has been captured!\n";
return std::string() + "oceanic " + std::to_string(a + b);
});
std::cout << func(4, 811) << '\n';
}
std::cout << "THE END!\n";
return 0;
}
Output:
Expected:
Pvt Ryan has been captured!
oceanic 815
Pvt Ryan has been executed
THE END
Actual:
Pvt Ryan has been executed
Pvt Ryan has been captured!
oceanic 815
Pvt Ryan has been executed
THE END!
https://godbolt.org/z/zc3d1Eave
What did I do wrong within the implementation?

It is not possible to elide the copy/move. The capture is constructed when the lambda expression is evaluated in the caller resulting in a temporary object. That lambda is then passed to the constructor and the constructor explicitly constructs a new object of that type in the storage by copy/move from the passed lambda. You can't identify the object created by placement-new with the temporary object passed to the constructor.
The only way to resolve such an issue is by not constructing the lambda with the capture that should not be copied/moved in the caller at all, but to instead pass a generator for the lambda which is then evaluated by the constructor when constructing the new object with placement-new. Something like:
template <typename CallableGenerator, typename... Args>
constexpr explicit static_function(CallableGenerator&& generator, Args&&... args)
: pVTable_(std::addressof(v_table::template get<std::invoke_result_t<CallableGenerator, Args...>>()))
{
static_assert(sizeof(std::decay_t<std::invoke_result_t<CallableGenerator, Args...>>) <= static_size, "Callable type is too big!");
new (&data_) auto(std::invoke(std::forward<CallableGenerator>(generator), std::forward<Args>(args)...);
}
//...
const auto &func = static_function<std::string(int, int)>([](auto str){
return [captured = prisoner{str}](int a, int b) -> std::string {
std::cout << captured.name << " has been captured!\n";
return std::string() + "oceanic " + std::to_string(a + b);
});
}, "Pvt Ryan");
// or
const auto &func = static_function<std::string(int, int)>([str="Pvt Ryan"]{
return [captured = prisoner{str}](int a, int b) -> std::string {
std::cout << captured.name << " has been captured!\n";
return std::string() + "oceanic " + std::to_string(a + b);
});
});
This requires C++17 or later to guarantee that the copy/move is elided. Before C++17 the elision cannot be guaranteed in this way with a lambda. Instead a manually-defined function object must be used so that its constructor can be passed the arguments like I am doing here with the generator. That would be the equivalent of emplace-type functions of standard library types.

Related

Is storing a reference to a (possibly) temporary object legal as long as the reference doesn't outlive the object?

After reading about std::variant and the handy overloaded trick I set myself a challenge (an exercise) to write a helper that would allow me to write
visit(variant).with([](int){}, [](char){});
instead of
std::visit(overloaded{[](int){}, [](char){}}, variant);
I'd also like it to work in other cases, like
VariantT variant;
VisitorT visitor;
visit(variant).with(visitor);
visit(VariantT{std::in_place_type<int>, 1}).with(visitor);
const VariantT constVariant;
visit(constVariant).with([](auto&&){});
You get the idea.
Here's what I came up with (note that I've changed the name from overloaded to Visitor:
#include <iostream>
#include <string>
#include <utility>
#include <variant>
template <typename... T>
struct Visitor : public T...
{
using T::operator()...;
};
template <typename... T>
Visitor(T...)->Visitor<T...>;
template <typename VariantT>
struct Helper
{
Helper(VariantT& variant)
: m_variant{variant}
{}
Helper(const Helper&) = delete;
Helper(Helper&&) = delete;
Helper& operator=(const Helper&) = delete;
Helper& operator=(Helper&&) = delete;
~Helper() = default;
template <typename VisitorT, typename... VisitorsT>
decltype(auto) with(VisitorT&& visitor,
VisitorsT&&... visitors) && // this function is ref-qualified so we can only call this on a temp object and we can
// be sure that the variant lives at least as long as us
{
if constexpr (sizeof...(visitors) == 0)
{
return std::visit(std::forward<VisitorT>(visitor), m_variant);
}
else
{
return std::visit(Visitor{visitor, visitors...}, m_variant);
}
}
private:
VariantT& m_variant;
};
template <typename VariantT>
decltype(auto) visit(VariantT&& variant)
{
// no forwarding here, even if an rvalue was passed, pass an lvalue ref
return Helper{variant};
}
int main()
{
visit(std::variant<int>{std::in_place_type<int>, -7})
.with([](int i) { std::cout << "got an int: " << i << std::endl; },
[](std::string str) { std::cout << "got a string: " << str << std::endl; });
std::variant<int, std::string> v = "String";
visit(v).with([](int i) { std::cout << "got an int: " << i << std::endl; },
[](std::string str) { std::cout << "got a string: " << str << std::endl; });
visit(v).with([](int& i) { i += 7; },
[](std::string& str) { str += "modified"; });
std::cout << visit(v).with([](int i) { return std::to_string(i); },
[](std::string str) { return str; }) << std::endl;
}
The question is: is storing references in the Helper like that perfectly legal?
My understanding is that temporary objects live until the end of the expression, so I guess this is ok?
Will it do The Right Thing? Are there any pitfalls in that code?
I've tested this code on both msvc and gcc and I'm not seeing anything wrong, but it doesn't mean this works fine in all cases.
The only obvious problem is that you can have things like
decltype(auto) silly() {
std::variant<int, std::string> v = "String";
return visit(v);
}
int main()
{
silly().with([](auto){ std::cout << "dangling" << std::endl; };
}
But such things are relatively easy to spot in review.

Template parameters are arranged in the wrong order. Is it correct?

I expect the following output in the following program: 2 1.2. But the list of template parameters is reversed (as I understand). Should it be like that?
void print_id(int i, double d) noexcept {
std::cout << i << ' ' << d << std::endl;
}
template <typename G>
int mycode(G item, std::string & p) noexcept {
p.append((const char*)&item, sizeof(G));
return 1;
}
template<typename G>
const char* iterate(const char* &p) noexcept {
// std::cout << (typeid(G)).name() << " "; It gets know that the first type is 'double', the next is 'int'
const char* ans = p;
p += sizeof(G);
return ans;
}
template<typename ...T>
std::function<void(const char*)> state_f(void(*func)(T...)) {
return [func](const char* p) {
func(*(const T*)(iterate<T>(p))...);
};
}
template<typename ...T>
std::string encode(T... tpl) noexcept {
std::string s;
int crutch[] = { mycode<T>(tpl, s)... };
return s;
}
int main(void)
{
auto f = state_f(print_id);
f(encode(2, 1.2).c_str());
return 0;
}
I can reverse the parameters but I don't think it is correct.
The key lines in the shown code:
int crutch[] = { mycode<T>(tpl, s)... };
The parameter pack will expand, basically, to:
int crutch[] = { mycode<double>(1.3, s), mycode<int>(2, s) };
The implementation of mycode, to make a long story short, appends its argument to a buffer.
The issue here is that in this instance, C++ does not have a guaranteed evaluation order. Either function call may get executed first, and it may very well be different each time you run the same program. You are not guaranteed a left-to-right evaluation order, in this context. Either argument may end up being appended to the buffer, first.

Different behavior needed if template template parameter is vector

We are using one internal c++ library that takes std::vector as input however i want to write wrapper function that should be able to accept std::vector, std::set or std::unordered_set but when input passed to this wrapper function is std::vector itself i don't want to copy that in temporary vector so is there any way to avoid this unnecessary copy.
Sample Reference code will explain this issue with more clarity :
#include <iostream>
#include <set>
#include <vector>
#include <unordered_set>
void print(const std::vector<int>& argVec)
{
for(auto elem:argVec)
{
std::cout<<elem<<std::endl;
}
}
template<template<typename...>class VecOrSet>
void wrapper(const VecOrSet<int>& input)
{
//How to avoid this temporary vector if input argument is vector itself
std::vector<int> temp(input.begin(),input.end());
print(temp);
}
int main()
{
std::vector<int> v{1,2,3};
std::set<int> s{4,5,6};
std::unordered_set<int> us{7,8,9};
wrapper(v);
wrapper(s);
wrapper(us);
return 0;
}
You can add a full specialization.
template<>
void wrapper(const std::vector<int>& input)
{
print(input);
}
Or just add another overload.
void wrapper(const std::vector<int>& input)
{
print(input);
}
Or use constexpr if (since C++17).
template<template<typename...>class VecOrSet>
void wrapper(const VecOrSet<int>& input)
{
if constexpr (std::is_same_v<VecOrSet<int>, std::vector<int>>) {
print(input);
} else {
std::vector<int> temp(input.begin(),input.end());
print(temp);
}
}
Another solution could be initialize temp passing through another function: getIntVect(), with a generic version
template <typename VoS>
std::vector<int> getIntVect (VoS const & input)
{ return { input.cbegin(), input.cend() }; }
that copy the input in a std::vector<int>, and a specific version for std::version<int>
std::vector<int> const & getIntVect (std::vector<int> const & input)
{ return input; }
that return a const-reference to the input (that is: avoid the copy)
So, using decltype(auto),
template<template<typename...>class VecOrSet>
void wrapper(const VecOrSet<int>& input)
{
decltype(auto) temp { getIntVect(input) };
print( temp );
}
temp is a reference to input, when VecOrSet is std::vector, or a copy of input, otherwise.
-- EDIT --
The OP is dubious
I think this line decltype(auto) temp{getIntVect(input)}; will call copy constructor of vector if input is vector
This is true for auto temp{getIntVect(input)};, as pointed by T.C. (thanks!). Not for decltype(auto) temp{getIntVect(input)};
Try compiling and running the following code
#include <iostream>
struct A
{
A ()
{ std::cout << "- default constructor" << std::endl; }
A (A const &)
{ std::cout << "- copy constructor" << std::endl; }
A (A &&)
{ std::cout << "- move constructor" << std::endl; }
};
template <typename T>
A foo (T const &)
{ return {}; }
A const & foo (A const & a)
{ return a; }
int main ()
{
std::cout << "--- 000" << std::endl;
A a0;
std::cout << "--- 001" << std::endl;
auto a1 { foo(a0) };
std::cout << "--- 002" << std::endl;
decltype(auto) a2 { foo(a0) };
std::cout << "--- 003" << std::endl;
decltype(auto) a3 { foo(0) };
std::cout << "--- 004" << std::endl;
}
I get (from g++ and clang++) this output
--- 000
- default constructor
--- 001
- copy constructor
--- 002
--- 003
- default constructor
--- 004
As you can see, auto a1 { foo(a0) }; call the copy constructor of A (because auto become A and A a1 { foo(a0) }; cause the the copy of the value returned by foo()) but decltype(auto) a2 { foo(a0) }; doesn't call contructors (because decltype(auto) become A const & and A const & a2 { foo(a0) }; simply link a2 to foo(a0) (so to a0).

C++ function decorator

I am looking for a way to decorate functions or lambdas in C++. The goal is to do something before and after the function call. As I've seen the closest thing to use is std::function but it needs to have the types of its arguments.
class FunctionDecorator
{
public:
FunctionDecorator( std::function func )
: m_func( func )
void operator()()
{
// do some stuff prior to function call
m_func();
// do stuff after function call
}
private:
std::function m_func;
};
It would be great if by template type could be used in std::function and it could deduce it somehow when i pass pointer to a function or a result from std::bind.
Is such thing possible in C++.
Hmm. I may or may not have gone overkill.
#include <type_traits>
#include <utility>
#include <iostream>
template <class T>
struct RetWrapper {
template <class Tfunc, class... Targs>
RetWrapper(Tfunc &&func, Targs &&... args)
: val(std::forward<Tfunc>(func)(std::forward<Targs>(args)...)) {}
T &&value() { return static_cast<T &&>(val); }
private:
T val;
};
template <>
struct RetWrapper<void> {
template <class Tfunc, class... Targs>
RetWrapper(Tfunc &&func, Targs &&... args) {
std::forward<Tfunc>(func)(std::forward<Targs>(args)...);
}
void value() {}
};
template <class Tfunc, class Tbefore, class Tafter>
auto decorate(Tfunc &&func, Tbefore &&before, Tafter &&after) {
return [
func = std::forward<Tfunc>(func),
before = std::forward<Tbefore>(before),
after = std::forward<Tafter>(after)
] (auto &&... args) -> decltype(auto) {
before(std::forward<decltype(args)>(args)...);
RetWrapper<std::result_of_t<Tfunc(decltype(args)...)>> ret(
func, std::forward<decltype(args)>(args)...
);
after(std::forward<decltype(args)>(args)...);
return ret.value();
};
}
/*
* Tests
*/
float test1(float a, float b) {
std::cout << "Inside test1\n";
return a * b;
}
void test2() {
std::cout << "Inside test2\n";
}
int i = 0;
int &test3() {
return i;
}
int main() {
auto test1Deco = decorate(
test1,
[] (float a, float b) {
std::cout << "Calling test1 with " << a << " and " << b << '\n';
},
[] (float a, float b) {
std::cout << "Called test1 with " << a << " and " << b << '\n';
}
);
float c = test1Deco(3.5f, 5.1f);
std::cout << "Yields " << c << '\n';
auto test2Deco = decorate(
test2,
[] () {
std::cout << "Calling test2\n";
},
[] () {
std::cout << "Called test2\n";
}
);
test2Deco();
auto test3Deco = decorate(
test3,
[] () {
std::cout << "Calling test3\n";
},
[] () {
std::cout << "Called test3\n";
}
);
auto &i2 = test3Deco();
i2 = 42;
std::cout << "Global i = " << i << '\n';
return 0;
}
Output :
Calling test1 with 3.5 and 5.1
Inside test1
Called test1 with 3.5 and 5.1
Yields 17.85
Calling test2
Inside test2
Called test2
Calling test3
Called test3
Global i = 42
Just go full template, without std::function:
template< typename Func >
class FunctionDecorator
{
public:
FunctionDecorator( Func func )
: m_func( std::move(func) )
{}
void operator()()
{
// do some stuff prior to function call
m_func();
// do stuff after function call
}
private:
Func m_func;
};
template< typename Func >
FunctionDecorator<Func> decorate(Func func) {
return FunctionDecorator<Func>(std::move(func));
}
[Note: edited a few hours after initial posting]
This perhaps isn't exactly what the OP was looking for, but it's still relevant and hopefully useful to others looking for answers.
Let's say you have a couple of functions that have slightly different signatures:
void foo1(int& x){ cout << "foo1(" << x << ")\n";}
void foo2(double& x){ cout << "foo2(" << x << ")\n";}
and you'd like to wrap a decorator around them both so as to standardise their signatures, e.g. turn them both into void (*)(int).
Then you could do the following:
template<typename Q, void (*foo_p)(Q&)>
void wrapped(int x){
Q v = 42.2 + x;
foo_p(v);
}
int main(){
using foo_t = void (*)(int); // we coerce foo1 and foo2 into this type
foo_t k_int = wrapped<int, foo1>;
foo_t k_double = wrapped<double, foo2>;
k_int(-1); //cout: foo1(41)
k_double(-1); //cout: foo2(41.2)
return 0;
}
Using the example main I've given here, clang inlines the whole thing, which is a good sign but not quite what we wanted to check. If you make the example a bit more complex (see live here) you can see that it does indeed inline everything within each wrapper, i.e. foo1 and foo2 don't exist in standalone form, only in wrapped form.
Originally, I use a lambda in addition to the wrapped<...> template (making use of the fact that lambdas with no-capture can be converted to function pointers) but then I realized that the extra wrapping was redundant in this case.
This method should work for passing anything known at run time, which could even include a pointer to a mutable global (although that's getting pretty messy).
#include <iostream>
#include <string>
using namespace std;
template <typename TResult, typename TParams>
class CClassGenerique
{
typedef TResult (*uneFonction) (TParams);
public :
CClassGenerique (uneFonction f){ m_function = f; }
void operator () (TParams t) { m_function (t); }
private :
uneFonction m_function;
};
template <typename TResult, typename TParams>
TResult BasicFunction (TParams p)
{
TResult t=0;
std::cout<<" Value = " << p <<endl;
return t;
}
int main (int argc, char* argv[])
{
CClassGenerique<int, int> c1 (BasicFunction<int, int>);
CClassGenerique<int, char*> c2 (BasicFunction<int, char*>);
CClassGenerique<char*, char*> c3 (BasicFunction<char*, char*>);
c1(3);
c2("bonsoir");
c3("hello");
return 0;
}

How to forward T to function taking const &T as an argument

I want to be able to call arbitrary functions with arguments packed in variant class that is not in my control (let's call it blackbox). I have written a function template unpack<T> which extracts value from blackbox that needs to be specialized for target types. This works fine for arguments passed by value. However, I have no idea how to handle pass-by reference:
#include <string>
#include <functional>
#include <iostream>
#include <utility>
#include <type_traits>
/* Variant container */
struct blackbox
{
int int_value() const { return 42; }
bool bool_value() const { return true; }
std::string string_value() const { return "str"; }
};
/* Unpack function templates */
template<typename T>
T unpack(const blackbox &v)
{
static_assert(sizeof(T) == 0, "This template has to be specialized");
}
template<>
int unpack(const blackbox &v)
{
return v.int_value();
}
template<>
bool unpack(const blackbox &v)
{
return v.bool_value();
}
template<>
std::string unpack(const blackbox &v)
{
return v.string_value();
}
/* Call function with arguments extracted from blackbox */
template<typename T>
void call(std::function<void(T)> f, const blackbox &v)
{
f(unpack<T>(v));
}
/* Sample functions */
void f_int(int i) { std::cout << "f_int(" << i << ")" << std::endl; }
void f_bool(bool b) { std::cout << "f_bool(" << b << ")" << std::endl; }
void f_str(std::string s) { std::cout << "f_str(" << s << ")" << std::endl; }
void f_str_ref(const std::string &s) { std::cout << "f_str_ref(" << s << ")" << std::endl; }
int main()
{
blackbox b;
// direct call
f_str_ref(b.string_value());
// indirect call
call(std::function<void(int)>(f_int), b);
call(std::function<void(bool)>(f_bool), b);
call(std::function<void(std::string)>(f_str), b);
call(std::function<void(const std::string&)>(f_str_ref), b); //doesn't work
return 0;
}
I need unpack specialization that forwards std::string instances to functions that take const std::string& argument. Defining
template<>
const std::string& unpack(const blackbox &v)
{
return v.string_value();
}
obviously doesn't work, because reference to local variable is returned. Not defining unpack specialization for const std::string& causes static assertion to fail.
Ideally, unpack<std::string> should be used for const std::string&, but providing separate specialization would be sufficient.
What you need is std::decay<T> from <type_traits>, that removes cv-qualifiers and references from given type (unless this is a function or array type):
static_assert(std::is_same<std::decay<const std::string&>::type,
std::string>::value, "!");
That being said, you can use the following syntax:
f(unpack<typename std::decay<T>::type>(v));
so that any variation of [cv/&/&&] std::string type will evaluate to plain std::string.