I am trying to get a std::bad_variant_access exception when the variant is not in the list .But the below code doesn't work it returns a implicit converted ASCII int value
what changes should I do that variant is strict on type selection
#include <iostream>
#include <variant>
using namespace std;
struct printer
{
void operator()(int x) {
cout << x << "i"<<endl;
}
void operator()(float x) {
cout << x << "f"<<endl;
}
void operator()(double x)
{
cout << x << "d" << endl;;
}
};
int main() {
using my_variant = std::variant<int, float, double>;
my_variant v0('c');
try {
std::visit(printer{}, v0);
}
catch(const std::bad_variant_access& e) {
std::cout << e.what() << '\n';
}
return 0;
}
Output:
99i
Whereas I was expecting to get std::bad_variant_access exception
Code
std::visit will only trigger a bad_variant_access exception if the variant is valueless_by_exception (C++17, see N4659 23.7.3.5 [variant.status] )
What this means is that if you tried to set a variant value in a fashion that throws an exception, the variant is left in a "valueless" state, so visitation is not permitted.
To trigger it, we can change the code like so:
struct S{
operator int() const{throw 42;}
};
struct printer{//as before};
int main() {
using my_variant = std::variant<int, float, double>;
my_variant v0{'c'};
try{
v0.emplace<0>(S());
}catch(...){}
try {
std::visit(printer{}, v0);
}
catch(const std::bad_variant_access& e) {
std::cout << e.what() << '\n';
}
}
Demo
Frank already answered why you could construct your variant in the first place using a char (construction chosen via overload).
You can not trigger a bad_variant_access by attempting to first construct a variant in a fashion that will throw because [variant.ctor] dictates that the constructor will rethrow that exception (in this case int).
According to the documentation for std::variant:
That constructor of variant does the following:
Constructs a variant holding the alternative type T_j that would be selected by overload resolution for the expression F(std::forward(t)) if there was an overload of imaginary function F(T_i) for every T_i from Types... in scope at the same time. [...]
I.e. std::variant will get the first type that is constructible from the passed argument. In this case, int is constructible from a char, so the variant gets assigned as such.
Related
I am trying to create a map with string as key and a generic method as value in C++, but I do not know if that is even possible. I would like to do something like that:
void foo(int x, int y)
{
//do something
}
void bar(std::string x, int y, int z)
{
//do something
}
void main()
{
std::map<std::string, "Any Method"> map;
map["foo"] = &foo; //store the methods in the map
map["bar"] = &bar;
map["foo"](1, 2); //call them with parameters I get at runtime
map["bar"]("Hello", 1, 2);
}
Is that possible? If yes, how can I realise this?
You can type-erase the function types into a container, then provide a template operator(). This will throw std::bad_any_cast if you get it wrong.
N.B. because of the type erasure, you will have to specify exactly matching arguments at the call site, as e.g. std::function<void(std::string)> is distinct from std::function<void(const char *)>, even though both can be called with a value like "Hello".
#include <any>
#include <functional>
#include <map>
#include <string>
#include <iostream>
template<typename Ret>
struct AnyCallable
{
AnyCallable() {}
template<typename F>
AnyCallable(F&& fun) : AnyCallable(std::function(std::forward<F>(fun))) {}
template<typename ... Args>
AnyCallable(std::function<Ret(Args...)> fun) : m_any(fun) {}
template<typename ... Args>
Ret operator()(Args&& ... args)
{
return std::invoke(std::any_cast<std::function<Ret(Args...)>>(m_any), std::forward<Args>(args)...);
}
std::any m_any;
};
void foo(int x, int y)
{
std::cout << "foo" << x << y << std::endl;
}
void bar(std::string x, int y, int z)
{
std::cout << "bar" << x << y << z << std::endl;
}
using namespace std::literals;
int main()
{
std::map<std::string, AnyCallable<void>> map;
map["foo"] = &foo; //store the methods in the map
map["bar"] = &bar;
map["foo"](1, 2); //call them with parameters I get at runtime
map["bar"]("Hello, std::string literal"s, 1, 2);
try {
map["bar"]("Hello, const char *literal", 1, 2); // bad_any_cast
} catch (std::bad_any_cast&) {
std::cout << "mismatched argument types" << std::endl;
}
map["bar"].operator()<std::string, int, int>("Hello, const char *literal", 1, 2); // explicit template parameters
return 0;
}
The most (I cannot say best here) you can do is to use a signature erasure. That mean to convert the pointer to functions to a common signature type, and then convert them back to the correct signature before using them.
That can only be done in very special use cases (I cannot imagine a real world one) and will be highly unsecure: nothing prevent you to pass the wrong parameters to a function. In short: NEVER DO THIS IN REAL WORLD CODE.
That being said, here is a working example:
#include <iostream>
#include <string>
#include <map>
typedef void (*voidfunc)();
void foo(int x, int y)
{
std::cout << "foo " << x << " " << y << std::endl;
}
void bar(std::string x, int y, int z)
{
std::cout << "bar " << x << " " << y << " " << z << std::endl;
}
int main()
{
std::map<std::string, voidfunc> m;
m["foo"] = (voidfunc) &foo;
m["bar"] = (voidfunc)& bar;
((void(*)(int, int)) m["foo"])(1, 2);
((void(*)(std::string, int, int)) m["bar"])("baz", 1, 2);
return 0;
}
It gives as expected:
foo 1 2
bar baz 1 2
I could not find in standard whether this invokes or not Undefined Behaviour because little is said about function pointer conversions, but I am pretty sure that all common compilers accept that, because it only involve function pointers casting.
You cannot store functions with different signatures in a container like map, no matter if you store them as a function pointer or std ::function<WHATEVER>. The information about the signature of the function is one and only one in both cases.
The types for the value in map is one, meaning that the object stored in it are all of the same type.
So if your functions have all the same signature, then it's easy, otherwise, you have to abandon type safety and start walking in a very dangerous realm.
The one in which you erase the type information about the functions stored inside the map.
This translates to something like map<string, void*>.
Here is a simple example program:
using fn_string = function<void(const string&)>;
using fn_optional_string = function<void(const optional<string>&)>;
void foo(fn_string) { cout << "string" << endl; }
void foo(fn_optional_string) { cout << "optional string" << endl; }
int main()
{
foo([&](const string&){ });
foo([&](const optional<string>&){ }); // <-- ambiguous
return 0;
}
It has 2 overloads for foo() -- one taking function with string parameter and another with optional<string>.
Why is 2nd call to foo() ambiguous?
Is there a simple way to fix it? Without casts?
UPDATE
The above was an overly simplified example of the following real world problem I am trying to solve, which is:
using delegate = variant<
function<void()>,
function<void(const string&)>,
function<void(const optional<string>&)>
>;
struct foo
{
void add_delegate(delegate fn) { fns.push_back(std::move(fn)); }
vector<delegate> fns;
};
int main()
{
foo bar;
bar.add_delegate([&](){ });
bar.add_delegate([&](const string&){ });
bar.add_delegate([&](const optional<string>&){ }); // ERROR
return 0;
}
The last call to add_delegate fails to compile, as it can't decide between function<void(const string&)> and function<void(const optional<string>&)>.
My understanding was that the issue had to do with overload resolution (hence my original example). What change should I make to add_delegate to permit it to accept all 3 versions of lambdas?
Complete example can be found on Coliru.
A lambda is not a std::function<>. A std::function<R(Args...)> is a type-erasure value type that can store any copyable object that is call-compatible with R(Args...).
In one case above, R is void (which for a std::function means "I don't care what it returns), and Args... is std::string. A callable object is call-compatible with this if you can call it with a std::string rvalue.
This is true of both std::optional<std::string> and std::string.
There is no special overload for "exact match" -- all that matters is, call compatible or not.
There are a few ways to handle this.
template<std::size_t N>
struct overload_order : overload_order<N-1> {};
template<>
struct overload_order<0> {};
namespace details {
void foo(overload_order<1>, fn_string) { cout << "string" << endl; }
void foo(overload_order<0>, fn_optional_string) { cout << "optional string" << endl; }
}
template<class F>
void foo(F&& f) {
foo( overload_order<!std::is_same<std::decay_t<F>, fn_optional_string>{}>{}, std::forward<F>(f) );
}
now we first try the fn_string one, and only if that fails do we try fn_optional_string, unless the argument is already a fn_optional_string, in which case we dispatch directly to that overload.
Declare the argument specifically as a fun_optional_string.
I don't know what to type to keep the software from complaining about a code only answer, so here's a poem:
There is an old hack from Milpitas...
His motto, "No bug can defeat us." ...
His resolve never lapses ...
As he fires those synapses ...
Fueled by doughnuts, cold rice, and fajitas. ...
#include <functional>
#include <iostream>
#include <optional>
#include <string>
using namespace std;
using fn_string = function<void(const string&)>;
using fn_optional_string = function<void(const optional<string>&)>;
void foo(fn_string) { cout << "string" << endl; }
void foo(fn_optional_string) { cout << "optional string" << endl; }
int main()
{
foo([&](const string&){ });
fn_optional_string g = [&](const optional<string>&) {};
foo(g); // <-- not ambiguous
return 0;
}
Well, it turned out to be simpler than I expected. All I had to do was add the following overload to struct foo:
void add_delegate(fn_opt_val fn) { add_delegate(delegate{ std::move(fn) }); }
Here is complete code on Coliru.
I am trying to create a map with string as key and a generic method as value in C++, but I do not know if that is even possible. I would like to do something like that:
void foo(int x, int y)
{
//do something
}
void bar(std::string x, int y, int z)
{
//do something
}
void main()
{
std::map<std::string, "Any Method"> map;
map["foo"] = &foo; //store the methods in the map
map["bar"] = &bar;
map["foo"](1, 2); //call them with parameters I get at runtime
map["bar"]("Hello", 1, 2);
}
Is that possible? If yes, how can I realise this?
You can type-erase the function types into a container, then provide a template operator(). This will throw std::bad_any_cast if you get it wrong.
N.B. because of the type erasure, you will have to specify exactly matching arguments at the call site, as e.g. std::function<void(std::string)> is distinct from std::function<void(const char *)>, even though both can be called with a value like "Hello".
#include <any>
#include <functional>
#include <map>
#include <string>
#include <iostream>
template<typename Ret>
struct AnyCallable
{
AnyCallable() {}
template<typename F>
AnyCallable(F&& fun) : AnyCallable(std::function(std::forward<F>(fun))) {}
template<typename ... Args>
AnyCallable(std::function<Ret(Args...)> fun) : m_any(fun) {}
template<typename ... Args>
Ret operator()(Args&& ... args)
{
return std::invoke(std::any_cast<std::function<Ret(Args...)>>(m_any), std::forward<Args>(args)...);
}
std::any m_any;
};
void foo(int x, int y)
{
std::cout << "foo" << x << y << std::endl;
}
void bar(std::string x, int y, int z)
{
std::cout << "bar" << x << y << z << std::endl;
}
using namespace std::literals;
int main()
{
std::map<std::string, AnyCallable<void>> map;
map["foo"] = &foo; //store the methods in the map
map["bar"] = &bar;
map["foo"](1, 2); //call them with parameters I get at runtime
map["bar"]("Hello, std::string literal"s, 1, 2);
try {
map["bar"]("Hello, const char *literal", 1, 2); // bad_any_cast
} catch (std::bad_any_cast&) {
std::cout << "mismatched argument types" << std::endl;
}
map["bar"].operator()<std::string, int, int>("Hello, const char *literal", 1, 2); // explicit template parameters
return 0;
}
The most (I cannot say best here) you can do is to use a signature erasure. That mean to convert the pointer to functions to a common signature type, and then convert them back to the correct signature before using them.
That can only be done in very special use cases (I cannot imagine a real world one) and will be highly unsecure: nothing prevent you to pass the wrong parameters to a function. In short: NEVER DO THIS IN REAL WORLD CODE.
That being said, here is a working example:
#include <iostream>
#include <string>
#include <map>
typedef void (*voidfunc)();
void foo(int x, int y)
{
std::cout << "foo " << x << " " << y << std::endl;
}
void bar(std::string x, int y, int z)
{
std::cout << "bar " << x << " " << y << " " << z << std::endl;
}
int main()
{
std::map<std::string, voidfunc> m;
m["foo"] = (voidfunc) &foo;
m["bar"] = (voidfunc)& bar;
((void(*)(int, int)) m["foo"])(1, 2);
((void(*)(std::string, int, int)) m["bar"])("baz", 1, 2);
return 0;
}
It gives as expected:
foo 1 2
bar baz 1 2
I could not find in standard whether this invokes or not Undefined Behaviour because little is said about function pointer conversions, but I am pretty sure that all common compilers accept that, because it only involve function pointers casting.
You cannot store functions with different signatures in a container like map, no matter if you store them as a function pointer or std ::function<WHATEVER>. The information about the signature of the function is one and only one in both cases.
The types for the value in map is one, meaning that the object stored in it are all of the same type.
So if your functions have all the same signature, then it's easy, otherwise, you have to abandon type safety and start walking in a very dangerous realm.
The one in which you erase the type information about the functions stored inside the map.
This translates to something like map<string, void*>.
Recently I tried to reinvent scope guard via std::unique_ptr (NOTE: Deleter has the member typedef pointer — is a specially handled case of std::unique_ptr):
#include <type_traits>
#include <utility>
#include <memory>
#include <iostream>
#include <cstdlib>
#include <cassert>
namespace
{
template< typename lambda >
auto
make_scope_guard(lambda && _lambda)
{
struct lambda_caller
{
using pointer = std::decay_t< lambda >;
void
operator () (lambda & l) const noexcept
{
std::forward< lambda >(l)();
}
};
return std::unique_ptr< std::decay_t< lambda >, lambda_caller >(std::forward< lambda >(_lambda));
}
}
int
main()
{
std::cout << 1 << std::endl;
{
std::cout << 2 << std::endl;
[[gnu::unused]] auto && guard_ = make_scope_guard([&] { std::cout << __PRETTY_FUNCTION__ << std::endl; });
std::cout << 3 << std::endl;
}
std::cout << 5 << std::endl;
return EXIT_SUCCESS;
}
Such an approach works fine for simple pointer to free function void f() { std::cout << 4 << std::endl; } passed to make_scope_guard, but not for any lambda passed to make_scope_guard.
This is due to an abundance of ... = pointer() into the std::unique_ptr definition (function default parameter, defaulting data memebers etc), but I can't find the DefaultConstructible requirement for pointer into this article.
Is it mandatory, that the pointer should match the std::is_default_constructible requirement?
It tested against libc++ and against libstdc++ using not too old clang++ -std=gnu++1z.
Seems, there should be language extension for lambdas: if auto l = [/* possible capture list */] (Args...) { /* code */; }; then using L = decltype(l); is equivalent to struct L { constexpr void operator () (Args...) const noexcept { ; } }; for some Args..., isn't it?
ADDITIONAL:
Providing the instance D{} of following DefaultConstructible class to make_scope_guard(D{}) requires commented out code to be uncommented in the context if (p) { ..., where p is of type D:
struct D { void operator () () const noexcept { std::cout << __PRETTY_FUNCTION__ << std::endl; } /* constexpr operator bool () const { return true; } */ };
A unique_ptr is still a pointer. You cannot shoehorn a lambda into it. From [unique.ptr]:
A unique pointer is an object that owns another object and manages that other object through a pointer.
More precisely, a unique pointer is an object u that stores a pointer to a second object p and will dispose of
p when u is itself destroyed
[...]
Additionally, u can, upon request, transfer ownership to another unique pointer u2. Upon completion of
such a transfer, the following post-conditions hold: [...] u.p is equal to nullptr
A lambda is not a pointer. A lambda cannot equal nullptr.
That said, you're already making your own local struct, why not just use that to do the RAII scope guarding itself instead of deferring to unique_ptr? That seems like a hack at best, and takes more code to boot. You could instead just do:
template< typename lambda >
auto
make_scope_guard(lambda && _lambda)
{
struct lambda_caller
{
lambda _lambda;
~lambda_caller()
{
_lambda();
}
};
return lambda_caller{std::forward<lambda>(_lambda)};
}
If you need to support release, you can wrap _lambda inside of boost::optional so that lambda_caller becomes:
struct lambda_caller
{
boost::optional<lambda> _lambda;
~lambda_caller()
{
if (_lambda) {
(*_lambda)();
_lambda = boost::none;
}
}
void release() {
_lambda = boost::none;
}
};
As I understood all types of boost.variant are parsed into real types (meaning as if boost variant<int, string> a; a="bla-bla" would after compilation turn into string a; a="bla-bla") And so I wonder: how to get what type was put into boost variant?
What have I tried:
#include <boost/variant.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <iostream>
int main()
{
typedef boost::function<double (double x)> func0;
typedef boost::function<double (double x, double y)> func1;
typedef boost::variant<int, func0, func1> variant_func;
func1 fn = std::plus<double>();
variant_func v(fn);
std::cout << boost::get<func1>(v)(1.0, 1.0) << std::endl; // this works
//std::cout << boost::get<v::type>(v)(1.0, 1.0) << std::endl; // this does not compile with many errors
// std::cout << (v)(1.0, 1.0) << std::endl; // this fails with Error 1 error C2064: term does not evaluate to a function taking 2 arguments
std::cin.get();
return 0;
}
v.which() will return the 0-based index of the type of the object currently held.
When you are retrieving the object your code must use a static type (in order to satisfy the get<T> function template) to refer to an (effectively) dynamically typed object.
You need to either test for the type (using which() or type()) and branch accordingly or use a static visitor. No matter which way you choose, you have to explicitly state the static type that you want to retrieve and it has to match the dynamic type or an exception will be thrown.
One way around this problem is instead of using a variant type directly, use a class which contains a variant type internally and then defines any implicit conversion operators necessary to use the object with minimum fuss.
I have a project called Dynamic C++ which uses this technique.
boost.variant has a .type() function which can return the typeid of the active type, provided you've enabled RTTI.
You could also define a static visitor to perform actions depending on the type of content of the variant, e.g.
struct SomeVisitor : public boost::static_visitor<double>
{
double operator()(const func0& f0) const { return f0(1.0); }
double operator()(const func1& f1) const { return f1(1.0, 1.0); }
double operator()(int integer) const { return integer; }
};
...
std::cout << boost::apply_visitor(SomeVisitor(), v) << std::endl;
You can use the following that both result in std::type_info objects:
the type() member function of boost::variant,
the C++ operator typeid() that can be applied to any type or typed expression,
together with the member function std::type_info::operator==, to check which type the boost::variant is currently storing. For example,
boost::variant<int, bool, std::string> container;
container = "Hello world";
if (container.type() == typeid(std::string)) {
std::cout << "Found a string: " << boost::get<std::string>(container);
}
else if (container.type() == typeid(int)) {
std::cout << "Found an int: " << boost::get<int>(container);
}
You can use the pointer version of boost::get. The tutorial has this example:
void times_two( boost::variant< int, std::string > & operand )
{
if ( int* pi = boost::get<int>( &operand ) )
*pi *= 2;
else if ( std::string* pstr = boost::get<std::string>( &operand ) )
*pstr += *pstr;
}
So you use it like you normally would use boost::get but pass a pointer to a variant instead, and the result is a pointer which is nullptr if that is not the type currently stored in the variant. It's not useful if that type appears more than once in the list of types in the variant, but that is not very common.