Using boost:variant:
#include <tuple>
#include <iostream>
#include <boost/variant.hpp>
template <size_t n, typename... T>
boost::variant<T...> _tuple_index(size_t i, const std::tuple<T...>& tpl) {
if (i == n)
return std::get<n>(tpl);
else if (n == sizeof...(T) - 1)
throw std::out_of_range("Out of Index");
else
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl);
}
template <typename... T>
boost::variant<T...> tuple_index(size_t i, const std::tuple<T...>& tpl) {
return _tuple_index<0>(i, tpl);
}
template <typename T>
auto tuple_len(T &tpl) {
return std::tuple_size<T>::value;
}
int main()
{
std::tuple<std::string, double, double, int> t("123", 4.5, 6.7, 8);
for(int i = 0; i != tuple_len(t); ++i) {
std::cout << tuple_index(i, t) << std::endl; // works with boost
}
}
Replace boost::variant by std::variant, added a helper to stream std::variant:
#include <tuple>
#include <iostream>
#include <variant>
template <size_t n, typename... T>
std::variant<T...> _tuple_index(size_t i, const std::tuple<T...>& tpl) {
if (i == n)
return std::get<n>(tpl);
else if (n == sizeof...(T) - 1)
throw std::out_of_range("Out of Index");
else
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl);
}
template <typename... T>
std::variant<T...> tuple_index(size_t i, const std::tuple<T...>& tpl) {
return _tuple_index<0>(i, tpl);
}
template <typename T>
auto tuple_len(T &tpl) {
return std::tuple_size<T>::value;
}
// added helper to stream std::variant
template <typename T0, typename ... Ts>
std::ostream & operator<< (std::ostream & s, std::variant<T0, Ts...> const & v) {
std::visit([&](auto && arg){ s << arg;}, v);
return s;
}
int main()
{
std::tuple<std::string, double, double, int> t("123", 4.5, 6.7, 8);
for(int i = 0; i != tuple_len(t); ++i) {
std::cout << tuple_index(i, t) << std::endl; // doesn't work anymore
}
}
The compilation remain error:
$ clang++ -v [17:37:47]
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
$ clang++ -std=c++17 isostd.cpp
isostd.cpp:8:16: error: no viable conversion from returned value of type 'const typename tuple_element<1UL, tuple<basic_string<char>, double, double, int> >::type' (aka 'const __type_pack_element<1UL, std::__1::basic_string<char>, double, double, int>') to function return
type 'std::variant<basic_string<char>, double, double, int>'
return std::get<n>(tpl);
^~~~~~~~~~~~~~~~
isostd.cpp:12:16: note: in instantiation of function template specialization '_tuple_index<1, std::__1::basic_string<char>, double, double, int>' requested here
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl);
^
isostd.cpp:16:12: note: in instantiation of function template specialization '_tuple_index<0, std::__1::basic_string<char>, double, double, int>' requested here
return _tuple_index<0>(i, tpl);
^
isostd.cpp:35:22: note: in instantiation of function template specialization 'tuple_index<std::__1::basic_string<char>, double, double, int>' requested here
std::cout << tuple_index(i, t) << std::endl; // doesn't work anymore
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/variant:1142:3: note: candidate constructor not viable: no known conversion from 'const typename tuple_element<1UL, tuple<basic_string<char>, double, double, int> >::type'
(aka 'const __type_pack_element<1UL, std::__1::basic_string<char>, double, double, int>') to 'const std::__1::variant<std::__1::basic_string<char>, double, double, int> &' for 1st argument
variant(const variant&) = default;
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/variant:1143:3: note: candidate constructor not viable: no known conversion from 'const typename tuple_element<1UL, tuple<basic_string<char>, double, double, int> >::type'
(aka 'const __type_pack_element<1UL, std::__1::basic_string<char>, double, double, int>') to 'std::__1::variant<std::__1::basic_string<char>, double, double, int> &&' for 1st argument
variant(variant&&) = default;
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/variant:1155:13: note: candidate template ignored: substitution failure [with _Arg = const double &, $1 = 0, $2 = 0, $3 = 0, _Tp = double]: no member named 'value' in
'std::__1::__find_detail::__find_unambiguous_index_sfinae<double, std::__1::basic_string<char>, double, double, int>'
constexpr variant(_Arg&& __arg) noexcept(
^
1 error generated.
How can I properly replace boost:variant by std::variant?
I aware of the reference: What are the differences between std::variant and boost::variant?
Boost.Variant includes recursive_variant, which allows a variant to contain itself. They're essentially special wrappers around a pointer to a boost::variant, but they are tied into the visitation machinery.
If I understand correctly, there is no way to finish the replace?
As you have duplicate types in your variant some of the constructors are disabled:
This overload only participates in overload resolution if there is exactly one occurrence of T in Types...
You need to use the constructor with an explicit type index:
return std::variant<T...>(std::in_place_index<n>, std::get<n>(tpl));
Your original boost code has undefined behaviour:
Each type specified as a template argument to variant must be distinct after removal of qualifiers. Thus, for instance, both variant<int, int> and variant<int, const int> have undefined behavior.
The standard libary implementation does support duplicate types and therefore prevents you from accidentally constructing an ambigous variant. For example what should the following do:
variant<std::string, double, double, int> t = 4.5;
With boost it is UB, either of the double values might be initialised or it might do something completely different. The standard library explitly makes this a compiler error so that you have to choose which of your doubles you want to initialise.
Related
Consider a custom type that is meant to multiply and divide a specific instantiation of a duration:
#include <chrono>
#include <iostream>
class Foo {};
using Duration = std::chrono::seconds;
inline Duration operator*(Duration d, Foo) {
std::cout << "multiplying some time with Foo\n";
return d;
}
inline Duration operator/(Duration d, Foo) {
std::cout << "dividing some time by Foo\n";
return d;
}
int main() {
Duration d;
Foo f;
d * f;
d / f;
}
This code compiles without warnings with gcc, but fails with clang (wandbox)
In file included from prog.cc:1:
/opt/wandbox/clang-7.0.0/include/c++/v1/chrono:1259:81: error: no type named 'type' in 'std::__1::common_type<long long, Foo>'
typename common_type<typename _Duration::rep, _Rep2>::type>::value>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
/opt/wandbox/clang-7.0.0/include/c++/v1/chrono:1272:7: note: in instantiation of default argument for '__duration_divide_imp<std::__1::chrono::duration<long long, std::__1::ratio<1, 1> >, Foo>' required here
: __duration_divide_imp<duration<_Rep1, _Period>, _Rep2>
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/wandbox/clang-7.0.0/include/c++/v1/chrono:1279:10: note: in instantiation of template class 'std::__1::chrono::__duration_divide_result<std::__1::chrono::duration<long long, std::__1::ratio<1, 1> >, Foo, false>' requested here
typename __duration_divide_result<duration<_Rep1, _Period>, _Rep2>::type
^
prog.cc:22:7: note: while substituting deduced template arguments into function template 'operator/' [with _Rep1 = long long, _Period = std::__1::ratio<1, 1>, _Rep2 = Foo]
d / f;
Note that operator* works just fine with both compilers.
The actual code is a bit more convoluted, using a friend method defined within a class-scope type which performs overflow-safe integer operation on durations, but shows exactly the same symptoms.
The issue looks similar to: User-defined overloaded operator * with std::chrono::duration, but that's a different operator and compiler.
This looks like a libc++ bug to me (and a bug I wrote). Here is a very lightly tested fix:
--- a/include/chrono
+++ b/include/chrono
## -1289,7 +1289,12 ## struct __duration_divide_result<duration<_Rep1, _Period>, _Rep2, false>
template <class _Rep1, class _Period, class _Rep2>
inline _LIBCPP_INLINE_VISIBILITY
_LIBCPP_CONSTEXPR
-typename __duration_divide_result<duration<_Rep1, _Period>, _Rep2>::type
+typename enable_if
+<
+ !__is_duration<_Rep2>::value &&
+ is_convertible<_Rep2, typename common_type<_Rep1, _Rep2>::type>::value,
+ duration<typename common_type<_Rep1, _Rep2>::type, _Period>
+>::type
operator/(const duration<_Rep1, _Period>& __d, const _Rep2& __s)
{
typedef typename common_type<_Rep1, _Rep2>::type _Cr;
struct A
{
template <typename T>
constexpr explicit operator
std::enable_if_t<
std::is_same<std::decay_t<T>, int>{},
int
>() const noexcept
{
return -1;
}
};
int main()
{
A a;
std::cout << int(a) << std::endl;
}
The error is clang-7.0.1:
<source>:21:16: error: no matching conversion for functional-style cast from 'A' to 'int'
std::cout << int(a) << std::endl;
^~~~~
<source>:7:22: note: candidate template ignored: couldn't infer template argument 'T'
constexpr explicit operator
^
That pattern just doesn't work for conversion functions. The problem is, in order to determine if a is convertible to int, we look for an operator int() - but what we get is:
std::enable_if_t<std::is_same<std::decay_t<T>, int>{}, int>
That's a non-deduced context - so we don't find int.
You have to move the condition into a defaulted parameter:
template <typename T, std::enable_if_t<std::is_same_v<T, int>, int> = 0>
constexpr explicit operator T() const noexcept { return -1; }
This way, we can deduce T and then let SFINAE do its magic. Note that you don't need decay since we don't have any kind of reference.
Catch2 gives a Predicate class to make our own matcher. https://github.com/catchorg/Catch2/blob/master/docs/matchers.md
I simply test an unordered_map(decltype(getEntity2IdMap()) here.
namespace Generic {
Predicate<decltype(getEntity2IdMap())>(
[&](auto& maps) -> bool {
return maps.size() == 3 &&
maps["entity1"] == 0 &&
maps["entity2"] == 1 &&
maps["entity3"] == 2; },
"entities were inserted."));
The Predicate class has a simple definition.
template <typename T>
class PredicateMatcher : public MatcherBase<T> {
std::function<bool(T const&)> m_predicate;
std::string m_description;
public:
PredicateMatcher(std::function<bool(T const&)> const& elem, std::string const& descr)
:m_predicate(std::move(elem)),
m_description(Detail::finalizeDescription(descr))
{}
bool match( T const& item ) const override {
return m_predicate(item);
}
std::string describe() const override {
return m_description;
}
};
} // namespace Generic
// The following functions create the actual matcher objects.
// The user has to explicitly specify type to the function, because
// infering std::function<bool(T const&)> is hard (but possible) and
// requires a lot of TMP.
template<typename T>
Generic::PredicateMatcher<T> Predicate(std::function<bool(T const&)> const& predicate, std::string const& description = "") {
return Generic::PredicateMatcher<T>(predicate, description);
}
However, clang++ yields a type mismatch.
error: no viable overloaded operator[] for type 'const std::__1::unordered_map<std::__1::basic_string<char>,
unsigned int, std::__1::hash<std::__1::basic_string<char> >, std::__1::equal_to<std::__1::basic_string<char> >,
std::__1::allocator<std::__1::pair<const std::__1::basic_string<char>, unsigned int> > >'
I am wondering the maps type here or I misunderstood the lambda context for "/m/entity1".
The full error message is likely to be something like:
<source>:7:21: error: no viable overloaded operator[] for type 'const std::unordered_map<std::string, int>' (aka 'const unordered_map<basic_string<char>, int>')
std::cout << map["test" ] == 1;
~~~^~~~~~~
unordered_map.h:963:7: note: candidate function not viable: 'this' argument has type 'const std::unordered_map<std::string, int>' (aka 'const unordered_map<basic_string<char>, int>'), but method is not marked const
operator[](const key_type& __k)
^
unordered_map.h:967:7: note: candidate function not viable: 'this' argument has type 'const std::unordered_map<std::string, int>' (aka 'const unordered_map<basic_string<char>, int>'), but method is not marked const
operator[](key_type&& __k)
The key clue is 'this' argument has type 'const.... but method is not marked const.
Your maps are const but operator[] is not const, you need to use find() or at() to retrieve values from a const std::map or std::unordered_map.
I'm currently exploring c++17 additions. After playing around with std::variant, wanted to use std::optional too, with the same example. Currently seeing that the compilation fails because of the following error:
error: no viable conversion from returned value of type
'(lambda at ./html_parser.hpp:53:9)' to function return type 'Parser<char>' (aka
'std::__1::function<std::__1::optional<std::__1::pair<char, std::__1::basic_string<char> > >
(std::__1::basic_string<char>)>')
return [=](std::string& input) -> ParserResult<char> {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/acid/tools/include/c++/v1/functional:1627:5: note: candidate constructor not viable: no known conversion
from '(lambda at ./html_parser.hpp:53:9)' to 'std::nullptr_t' (aka 'nullptr_t') for 1st argument
function(nullptr_t) _NOEXCEPT : __f_(0) {}
^
/home/acid/tools/include/c++/v1/functional:1628:5: note: candidate constructor not viable: no known conversion
from '(lambda at ./html_parser.hpp:53:9)' to 'const
std::__1::function<std::__1::optional<std::__1::pair<char, std::__1::basic_string<char> > >
(std::__1::basic_string<char>)> &' for 1st argument
function(const function&);
^
/home/acid/tools/include/c++/v1/functional:1629:5: note: candidate constructor not viable: no known conversion
from '(lambda at ./html_parser.hpp:53:9)' to 'std::__1::function<std::__1::optional<std::__1::pair<char,
std::__1::basic_string<char> > > (std::__1::basic_string<char>)> &&' for 1st argument
function(function&&) _NOEXCEPT;
^
/home/acid/tools/include/c++/v1/functional:1631:5: note: candidate template ignored: requirement
'__callable<(lambda at ./html_parser.hpp:53:9)>::value' was not satisfied [with _Fp =
(lambda at ./html_parser.hpp:53:9)]
function(_Fp);
^
1 error generated.
To parse the HTML to give the DOM, started with declaring some parser combinators as follows:
#pragma once
#include <string>
#include <utility>
#include <functional>
#include <optional>
namespace dragon {
namespace html {
namespace parser {
template <typename ParserOutput, typename ParserInput = std::string>
using ParserResult = std::optional<std::pair<ParserOutput, ParserInput>>;
template<typename ParserOutput, typename ParserInput = std::string>
using Parser = std::function<ParserResult<ParserOutput, ParserInput>(ParserInput)>;
template <typename ParserOutput, typename ParserInput = std::string>
auto parse(Parser<ParserOutput, ParserInput> p, ParserInput i) -> ParserResult<ParserOutput, ParserInput>{
return p(i);
}
// few parser combinators.
// thenP combinator: applies the first parser, if it succeeds apply the second to the rest of
// the input left over by the first parser.
// currently just fails and returns empty!! does not provide any debugging info/msg
// as to why the parsing failed.
template<typename FirstParser, typename SecondParser>
auto thenP(FirstParser f, SecondParser s) {
return [=](std::string input) -> decltype(parse(s, std::string())) {
auto fv = parse(f, input);
if (fv) {
auto fvv = *fv;
return parse(s, fvv.second);
}
else {
return {};
}
};
}
template<typename FirstParser, typename SecondParser>
auto choiceP(FirstParser f, SecondParser s) {
return [=](std::string input) {
auto fv = parse(f, input);
if (!fv) return parse(s, input);
return fv;
};
}
auto charP(char match) -> Parser<char> {
return [=](std::string& input) -> ParserResult<char> {
if ((input.empty() == false) && (input[0] == match)) {
return std::make_pair(input[0], input.substr(1));
}
return {};
};
}
}
}
}
I'm seeing the above error, when trying to compile the simple use as shown below:
int main()
{
auto less = Parser::parser::charP('<');
auto greater = Parser::parser::charP('>');
auto lag = Parser::parser::thenP(less, greater);
auto log = Parser::parser::choiceP(less, greater);
auto lagv = lag("<>");
auto logv = log("|>");
return 0;
}
This compiles fine with Visual Studio 2017 (std=c++-latest). But Clang gives the above error. Trying to figure out the discrepancy between these two compilers. and how to fix this issue with Clang.
This is ill-formed:
auto charP(char match) -> Parser<char> {
return [=](std::string& input) -> ParserResult<char> { ... };
}
for the same reason that this is ill-formed:
std::function<void(int)> f = [](int& ){};
The lambda on the right is not invocable with an int, only an int&, so you can't construct a function<void(int)> out of it. MSVC has some permissive mode where it allows constructing a non-const lvalue reference from an rvalue, which is probably why it worked.
I came across some odd thing I would like to have an explanation for. The following code snippet provides a simple class template type and two operator<<s: one for specializations of type and one for a std::pair of type specializations.
#include <ostream>
#include <utility>
template <typename T>
class type {
public:
T value_;
};
template <typename CTy, typename CTr, typename T>
std::basic_ostream<CTy,CTr>&
operator<<(std::basic_ostream<CTy,CTr>& os, type<T> const& a)
{
return os << a.value_;
}
template <typename CTy, typename CTr, typename T>
std::basic_ostream<CTy,CTr>&
operator<<(std::basic_ostream<CTy,CTr>& os, std::pair<T const, T const> const& a)
{
return os << a.first << ',' << a.second;
}
#include <iostream>
int
main()
{
using float_type = type<float>;
float_type const a = { 3.14159 };
float_type const b = { 2.71828 };
#if 0
std::cout << std::make_pair(a, b)
<< std::endl;
#else
std::cout << std::pair<float_type const, float_type const>(a, b)
<< std::endl;
#endif
}
The main function provides a specialization and two variables of that specialization. There are two variants for displaying the variables as a std::pair. The first fails because std::make_pair seems to strip the const specifier from the variables, which in turn doesn't match with the signature of the second operator<<: std::pair<T const, T const>. However, constructing a std::pair specialization (second std::cout line in main) works as well as removing the const specification for T from operator<< for std::pair, i.e. std::pair<T, T>.
Compiler messages::
gcc 4.9.2
std_make_pair.cpp: In function 'int main()':
std_make_pair.cpp:52:35: error: cannot bind 'std::ostream {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&'
std::cout << std::make_pair(a, b) << std::endl;
^
In file included from std_make_pair.cpp:3:0:
/usr/include/c++/4.9.2/ostream:602:5: note: initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = std::pair<type<float>, type<float> >]'
operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)
^
clang 3.5 (non-viable functions from system headers removed)
std_make_pair.cpp:52:13: error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>')
and 'pair<typename __decay_and_strip<const type<float> &>::__type, typename __decay_and_strip<const
type<float> &>::__type>')
std::cout << std::make_pair(a, b) << std::endl;
~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~
std_make_pair.cpp:30:1: note: candidate template ignored: can't deduce a type for 'T' which would make
'const T' equal 'type<float>'
operator<<(std::basic_ostream<CTy,CTr>& os, std::pair<T const, T const> const& a)
so, here's the question: am I supposed to specify an operator<< taking a std::pair of T instead of T const? Isn't that watering down the contract I'm setting up with any user of the functionality, i.e. with T const I basically promise to use T only in non-mutating ways?
The first fails because std::make_pair seems to strip the const specifier from the variables, which in turn doesn't match with the signature of the second operator<<: std::pair<T const, T const>
That is correct. make_pair is a function template that relies on std::decay to explicitly drop const, volatile, and & qualifiers:
template <class T1, class T2>
constexpr pair<V1, V2> make_pair(T1&& x, T2&& y);
Returns: pair<V1, V2>(std::forward<T1>(x), std::forward<T2>(y));
where V1 and V2 are determined as follows: Let Ui be decay_t<Ti> for each Ti. Then each Vi is X& if Ui equals reference_wrapper<X>, otherwise Vi is Ui.
The compiler is completely correct to reject your code - you added a stream operator for pair<const T, const T>, but are trying to stream a pair<T, T>. The solution is to just remove the extra const requirement in your stream operator. Nothing in that function requires that the pair consist of const types - just that the types themselves are streamable, which is independent of their constness. There is nothing wrong with this:
template <typename CTy, typename CTr, typename T>
std::basic_ostream<CTy,CTr>&
operator<<(std::basic_ostream<CTy,CTr>& os, std::pair<T, T> const& a)
{
return os << a.first << ',' << a.second;
}
You're already taking the pair by reference-to-const, it's not like you can modify its contents anyway.