Are there any pitfalls to simulate defaulted function parameters using aggregate initialization? - c++

Question:
If you want, for example, a variadic function that takes arbitrary number of parameter Args&&...args, and prints all those arguments t times. What's more is that you want t to be defaulted to 1, so it prints all args one time by default.
The first thing you would try is:
template <typename ... Args>
void foo(Args... args, unsigned t = 1) {
for (unsigned i = 0; i < t; ++i) {
(std::cout << ... << args);
}
}
Apparently this doesn't work unless you explicitly pass in the template parameters:
// Error: expected 1 argument, got 2
foo(0, "Hello, world!");
Because default parameters are treated as normal parameters while template deduction, and the parameter pack would always be empty. This prevents you from making the function useful. (Related question)
I then decided to use aggregate initialization (especially designated initializer since c++ 20) to simulate a more powerful "default parameter". It looks like this:
struct foo_t {
unsigned t = 1;
template <typename ... Args>
void operator() (Args... args) {
for (unsigned i = 0; i < t; ++i) {
(std::cout << ... << args);
}
}
};
int main() {
foo_t{}("Hello, ", "World!\n"); // prints 1 line
foo_t{ 5 }(0, "Hello, world!\n"); // prints 5 lines
return 0;
}
Moreover, this may solve people's complaint that they cannot "skip" default function parameters, with the help of c++ 20 designated intializers:
struct bar_t {
const std::string& str = "Hello, world!";
int t = 1;
void operator() () {
for (int i = 0; i < t; ++i) {
std::cout << str << std::endl;
}
}
};
int main() {
// Skips .str, using the default "Hello, World!"
bar_t{ .t = 10 }();
return 0;
}
I am wondering whether there are any potential pitfalls to do this.
Background (Can be safely ignored)
So yesterday I was wandering around SO and encountered a question (but it was later deleted) that asked about how to combine default std::source_location parameter with variadic template:
template<typename... Args>
void log(Args&&... args, const std::experimental::source_location& location = std::experimental::source_location::current()) {
std::cout << location.line() << std::endl;
}
Apparently this does not work as expected, just as stated in the question. So I came up with the following code:
struct logger {
const std::experimental::source_location& location = std::experimental::source_location::current();
template <typename... Args>
void operator() (Args&&... args) {
std::cout << location.line() << std::endl;
}
};
int main(int argc, char** argv) {
logger{}("I passed", argc, "arguments.");
return 0;
}
But found out it can do more, thus this question.

There is at least one pitfall with lifetime (extension):
const std::string& str = "Hello, world!"; create dangling pointer, (No lifetime extension for member).
Following is fine:
void repeat_print(const std::string& str = "Hello, world!", int t = 1) {/*..*/}
int main()
{
repeat_print();
}
but following is not:
struct bar_t {
const std::string& str = "Hello, world!";
int t = 1;
void operator() () const { /*..*/ }
};
int main()
{
bar_t{}();
}
You might fix bar_t to take member by value, but then you would do extra copy in some cases.

Related

How to exit a function with a return value without using "return" in c++

How would I exit a function with a return value without using return.
Is there something like this in c++ :
auto random_function() {
printf("Random string"); // Gets executed
exit_with_return_value(/* Any random value. */);
printf("Same string as before"); // Doesn't get executed
}
Because I'm aware about exit() which takes a exit code.
But is there any way I could exit with a return value.
It is just that I can't call return is parentheses like this:
( return /* random value*/ );
But I can call functions in parentheses,
(exit(0));
My use case:
template <typename ...Parameters>
class Parameter_Pack
{
private:
void* paramsAddr[sizeof...(Parameters)];
public:
Parameter_Pack(Parameters ...parameters) {
size_t count = 0;
((
parameters,
this->paramsAddr[count] = malloc(sizeof(Parameters)),
*(Parameters*)paramsAddr[count] = parameters,
count++
), ...);
}
auto operator[](size_t index) {
size_t count = 0;
try {
(((count == index ? : return *
(Parameters*)paramsAddr[index] : *
(Parameters*)paramsAddr[index]), count++), ...);
} catch (Parameters...) {
std::cout << "Error: " << std::endl;
}
}
const size_t size() const {
return sizeof...(Parameters);
}
};
The problem is I can't return in auto operator[](size_t index).
The compiler error is :
"expected primary-expression before 'return'"
This doesn't answer your question directly, but instead of reinventing the wheel why not unpack the parameter pack into an std::tuple. You can then use std::get to access the object's by index.
#include <iostream>
#include <tuple>
template<typename ...Args>
static void unpack(Args&& ...args)
{
std::tuple pack{ std::forward<Args>(args)... };
int first = std::get<0>(pack);
std::cout << first << '\n';
const std::string& second = std::get<1>(pack);
std::cout << second << '\n';
bool third = std::get<2>(pack);
std::cout << std::boolalpha << third << '\n';
}
int main()
{
unpack(42, std::string{ "Some string" }, false);
}
OK, so the only thing I could come up with that kind of does what you want is to use a std::vector in conjunction with a std::variant.
Personally I think this would be an annoying API to use but it will allow you to return multiple types from the subscript operator and doesn't require a constant expression, i.e. index can be a runtime value.
#include <iostream>
#include <variant>
#include <vector>
#include <string>
template<typename ...Args>
class Pack {
public:
using Types = std::variant<Args...>;
Pack(Args... args)
: pack_{ std::move(args)... }
{}
Types& operator[](const std::size_t index) {
return pack_.at(index);
}
std::size_t size() const noexcept {
return pack_.size();
}
private:
std::vector<Types> pack_;
};
int main() {
Pack pack{42, std::string{ "Some string" }, false};
std::cout << pack.size() << '\n';
if (int* num = std::get_if<int>(&pack[0])) {
std::cout << "My num: " << *num << '\n';
}
}
return is a statement. Statements can't be part of a larger expression, so you can't return as a subexpression of some larger expression.
throw is an expression. It can be a subexpression of a larger expression, and you can throw any object you like.
It will be inconvenient for your callers, particularly if you mix it with an ordinary return. It will also not match the expectations other programmers have for how functions work. For that reason, I suggest you don't throw when you mean return.

How can I make it work,when passed "rvalue"?

code as follows:
#include <iostream>
#include<functional>
class TestCase {
public:
template<typename ...Args>
void myfun(Args&& ...args) {
sayhello(std::forward<Args>(args)...);
}
private:
void sayhello(const std::string& v, const std::string& v2) {
std::cout <<" ---1---- " << v << "," << v2 << std::endl;
}
};
template<typename ...Args>
void test(Args&& ...args) {
TestCase a;
auto f = std::bind(&TestCase::myfun<Args...>, &a,std::forward<Args>(args)...);
f();
}
int main() {
std::string a = "1";
std::string b = "2";
test(a,b); //ok
test("1","2"); //error
return 0;
}
why std::forward not work? I tried pass value by lvalue and it work,but pass value by rvalue not work.I confused with TestCase::myfun<Args...> ,should it be TestCase::myfun<Args&&...> or something else?
The problem is similar to that in std::bind and rvalue reference.
std::bind will always copy the arguments and store them. When you use "1", "2", TestCase::myfun<Args...> is instantiated with Args = const char &[2], while the arguments are stored as const char * (due to the copy), so the error occurs since the parameter const char &[2] does not match arguments of const char *.
When you use std::string("1"), std::string("2"), TestCase::myfun<Args...> is instantiated with Args = std::string (so the parameter type is std::string &&). The error still occurs since the parameter std::string && does not match the stored arguments, which are lvalues.
As you have found, using lambda is a proper solution.

How to check in compile time that the function has default parameter value?

All try to do is to make this piece of code
int main() {
if constexpr( ??? ) {
std::cout << "Yes\n";
std::cout << f() << '\n';
std::cout << f(42) << '\n';
}
else {
std::cout << "No\n";
}
return 0;
}
compile if the function f is defined as in any of these examples
// Example 1
int f(int n = 0) { return n; }
// Example 2
int f(int n) { return n; }
int f() { return 0; }
// Example 3
int f(int n) { return n; }
and display Yes for examples 1 and 2, and display No for the example 3.
Is this even possible? I think I've seen someone doing this with SFINAE but I don't remember how exactly it was done and where exactly I saw that. Thank you in advance.
if constexpr can’t protect ill-formed code outside of any template (e.g., in main). The obvious thing to do is to write a template that accepts f itself as a template argument, but to do that you have to reify your overload set. The usual way to do that is as a SFINAE-friendly function object:
template<class F,class=void>
constexpr bool opt=false;
template<class F>
constexpr bool opt<F,decltype(std::declval<F>()(1),void(std::declval<F>()()))> =true;
template<class F> int use(F &&x) {
if constexpr(opt<F>) return x(1)+x();
else return 0;
}
const auto f_=[](auto &&...aa) -> decltype(f(std::forward<decltype(aa)>(aa)...))
{return f(std::forward<decltype(aa)>(aa)...);};
int main() {use(f_);}
In some cases there is also the option of creating a “fake” template that uses calls that are formally dependent but always use the types you want, but that’s impossible for a call f() with no arguments, which is ill-formed (possibly with no diagnostic required) immediately if your f requires an argument since it can’t depend on a template parameter.

Did std::bind implement std::ref and std::cref to disambiguate the function call?

I know that I shouldn't overload a function for just parameters differ only in one of them passed by copy and the other by reference:
void foo(int x)
{
cout << "in foo(int x) x: " << x << endl;
}
void foo(int& x)
{
cout << "in foo(int& x) x: " << x << endl;
}
int main()
{
int a = 1;
foo(5); // ok as long as there is one best match foo(int)
foo(a); // error: two best candidates so the call is ambiguous
//foo(std::move(a));
//foo(std::ref(an)); // why also this doesn't work?
}
So a code that uses std::bind can be like this:
std::ostream& printVec(std::ostream& out, const std::vector<int> v)
{
for (auto i : v)
out << i << ", ";
return out;
}
int main()
{
//auto func = std::bind(std::cout, std::placeholders::_1); // error: stream objects cannot be passed by value
auto func = std::bind(std::ref(std::cout), std::placeholders::_1); // ok.
}
So std::ref here to ensure passing by reference rather than by value to avoid ambiguity?
* The thing that matters me: Does std::bind() implemented some wrapper to overcome this issue?
Why I can't use std::ref in my example to help the compiler in function matching?
Now that you know passing by value and reference are ambiguous when overload resolution tries to compare them for choosing a best viable function, let's answer how would you use std::ref (or std::cref) to differentiate between pass-by-value and pass-by-reference.
It turns out to be ... pretty simple. Just write the overloads such that one accepts a int, and the other accepts a std::reference_wrapper<int>:
#include <functional>
#include <iostream>
void foo(int x) {
std::cout << "Passed by value.\n";
}
void foo(std::reference_wrapper<int> x) {
std::cout << "Passed by reference.\n";
int& ref_x = x;
ref_x = 42;
/* Do whatever you want with ref_x. */
}
int main() {
int x = 0;
foo(x);
foo(std::ref(x));
std::cout << x << "\n";
return 0;
}
Output:
Passed by value.
Passed by reference.
42
The function pass the argument by value by default. If you want to pass by reference, use std::ref explicitly.
Now let's answer your second question: how does std::bind deal with this type of scenario. Here is a simple demo I have created:
#include <functional>
#include <type_traits>
#include <iostream>
template <typename T>
struct Storage {
T data;
};
template <typename T>
struct unwrap_reference {
using type = T;
};
template <typename T>
struct unwrap_reference<std::reference_wrapper<T>> {
using type = std::add_lvalue_reference_t<T>;
};
template <typename T>
using transform_to_storage_type = Storage<typename unwrap_reference<std::decay_t<T>>::type>;
template <typename T>
auto make_storage(T&& obj) -> transform_to_storage_type<T> {
return transform_to_storage_type<T> { std::forward<T>(obj) };
}
int main() {
int a = 0, b = 0, c = 0;
auto storage_a = make_storage(a);
auto storage_b = make_storage(std::ref(b));
auto storage_c = make_storage(std::cref(c));
storage_a.data = 42;
storage_b.data = 42;
// storage_c.data = 42; // Compile error: Cannot modify const.
// 0 42 0
std::cout << a << " " << b << " " << c << "\n";
return 0;
}
It is not std::bind, but the method used is similar (it's also similar to std::make_tuple, which has the same semantic). make_storage by default copies the parameter, unless you explicitly use std::ref.
As you can see, std::ref is not magic. You need to do something extra for it to work, which in our case is to first decay the type (all references are removed in this process), and then check whether the final type is a reference_wrapper or not; if it is, unwrap it.

C++11 Lambda Template Infering

So I have the following code:
#include <iostream>
template <typename T>
class funcky
{
public:
funcky(char const* funcName, T func)
: name(funcName), myFunc(func)
{
}
//private:
char const* name;
T myFunc;
};
#if 0
int main(void)
{
char const* out = "nothing";
// requires template args
funcky test("hello", [&](int x, int y) -> int
{
out = "YES";
return x + y;
});
std::cout << test.name << " = " << test.myFunc(1, 2) << std::endl;
std::cout << test.name << " = " << out << std::endl;
return 0;
}
int main2(void)
{
funcky<void(*)(void)> test("hello", [&, this](void) -> void
{
std::cout << this->name << std::endl;
});
test.myFunc();
return 0;
}
#endif
int main(void)
{
char const* out = "nothing";
auto myFunc = [&](int x, int y) -> int
{
out = "YES";
return x + y;
};
funcky<decltype(myFunc)> test("hello", myFunc);
std::cout << test.name << " = " << test.myFunc(1, 2) << std::endl;
std::cout << test.name << " = " << out << std::endl;
return 0;
}
The top chunk is a function holder that holds a lambda and a name for it.
Next is what I'd like to use API-wise, but fails due to no template arguments being specified.
After that, there's my wondering if it's possible to have a 'this' of a specific type (such as funcky) be used in a lambda not declared inside it. Wishful thinking.
At the very end is code that compiles but uses a lambda outside the funcky constructor and decltype.
Are such things possible in C++11? How I accomplish said things?
Also unless it can kind of have the same API, try not to guess what I'm doing as if I can't do it this way, I'll just rewrite it in a simpler way. It's not worth the effort.
If you want to provide a way for a user to supply a callback to your class, you're better off using std::function, since templating the class on the function / functor type is not a very useful thing to do, as you experienced.
The problem arises from the fact that you can't just take anything in. You should have clear requirements on what can be passed as a callback, since you should know how you want to call it later on. See this on why I make the constructor a template.
#include <functional>
#include <utility>
struct X{
template<class F>
X(F&& f) : _callback(std::forward<F>(f)) {} // take anything and stuff it in the 'std::function'
private:
std::function<int(int,int)> _callback;
};
int main(){
X x([](int a, int b){ return a + b; });
}
If, however, you don't know how the callback is going to be called (say, the user passes the arguments later on), but you want to support that, template your type on the signature of the callback:
#include <iostream>
#include <functional>
#include <utility>
template<class Signature>
struct X{
template<class F>
X(F&& f) : _callback(std::forward<F>(f)) {} // take anything and stuff it in the 'std::function'
private:
std::function<Signature> _callback;
};
int main(){
X<int(int,int)> x1([](int a, int b){ return a + b; });
X<void()> x2([]{ std::cout << "wuzzah\n";});
}
Something like
template<typename Functor>
funcky<typename std::decay<Functor>::type>
make_funcky(const char* name, Functor&& functor)
{ return { name, std::forward<Functor>(functor) }; }
can be helpful for things like:
auto test = make_funcky("hello", [&](int x, int y) -> int
{
out = "YES";
return x + y;
});
However, inside a lambda expression this always refers to the immediate this outside of the expression. It's not a delayed reference to some this present at the time of the invocation -- it's not an implicit parameter. As such it doesn't make sense to want 'another type' for it.