I've come across the pattern matching proposal in C++, and I tried to compile Example 4.6 about expression trees.
Here's the extracted code:
#include <memory>
#include <variant>
struct Expr;
struct Neg {
std::shared_ptr<Expr> expr;
};
struct Add {
std::shared_ptr<Expr> lhs, rhs;
};
struct Mul {
std::shared_ptr<Expr> lhs, rhs;
};
struct Expr : std::variant<int, Neg, Add, Mul> {
using variant::variant;
};
namespace std {
template <>
struct variant_size<Expr> : variant_size<Expr::variant> {};
template <std::size_t I>
struct variant_alternative<I, Expr> : variant_alternative<I, Expr::variant> {};
}
int eval(const Expr& expr) {
struct visitor {
int operator()(int i) const {return i;}
int operator()(const Neg& n) const {return -eval(*n.expr);}
int operator()(const Add& a) const {return eval(*a.lhs) + eval(*a.rhs);}
int operator()(const Mul& m) const {// Optimize multiplication by 0.
if (int* i = std::get_if<int>(m.lhs.get()); i && *i == 0) {
return 0;
}
if (int* i = std::get_if<int>(m.rhs.get()); i && *i == 0) {
return 0;
}
return eval(*m.lhs) * eval(*m.rhs);}
};
return std::visit(visitor{}, expr);
}
The code above gives 4 compilation errors, with:
clang++ -std=c++17 main.cpp
In file included from main.cpp:6:
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:778:6: error: implicit instantiation of undefined template 'std::__detail::__variant::_Extra_visit_slot_needed<int, const Expr &>::_Variant_never_valueless<Expr>'
&& !_Variant_never_valueless<__remove_cvref_t<_Variant>>::value;
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:809:44: note: in instantiation of static data member 'std::__detail::__variant::_Extra_visit_slot_needed<int, const Expr &>::value' requested here
_Extra_visit_slot_needed<_Ret, _Variant>::value ? 1 : 0;
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:821:53: note: in instantiation of static data member 'std::__detail::__variant::_Multi_array<int (*)(visitor &&, const Expr &), 4>::__do_cookie' requested here
_Multi_array<_Tp, __rest...> _M_arr[__first + __do_cookie];
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1016:36: note: in instantiation of template class 'std::__detail::__variant::_Multi_array<int (*)(visitor &&, const Expr &), 4>' requested here
static constexpr _Array_type _S_vtable
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1638:55: note: in instantiation of template class 'std::__detail::__variant::__gen_vtable<true, int, visitor &&, const Expr &>' requested here
constexpr auto& __vtable = __detail::__variant::__gen_vtable<
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1654:14: note: in instantiation of function template specialization 'std::__do_visit<false, true, visitor, const Expr &>' requested here
return __do_visit(std::forward<_Visitor>(__visitor),
^
main.cpp:48:15: note: in instantiation of function template specialization 'std::visit<visitor, const Expr &>' requested here
return std::visit(visitor{}, expr);
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:769:34: note: template is declared here
template <typename> struct _Variant_never_valueless;
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:809:2: error: constexpr variable '__do_cookie' must be initialized by a constant expression
_Extra_visit_slot_needed<_Ret, _Variant>::value ? 1 : 0;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:821:53: note: in instantiation of static data member 'std::__detail::__variant::_Multi_array<int (*)(visitor &&, const Expr &), 4>::__do_cookie' requested here
_Multi_array<_Tp, __rest...> _M_arr[__first + __do_cookie];
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1016:36: note: in instantiation of template class 'std::__detail::__variant::_Multi_array<int (*)(visitor &&, const Expr &), 4>' requested here
static constexpr _Array_type _S_vtable
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1638:55: note: in instantiation of template class 'std::__detail::__variant::__gen_vtable<true, int, visitor &&, const Expr &>' requested here
constexpr auto& __vtable = __detail::__variant::__gen_vtable<
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1654:14: note: in instantiation of function template specialization 'std::__do_visit<false, true, visitor, const Expr &>' requested here
return __do_visit(std::forward<_Visitor>(__visitor),
^
main.cpp:48:15: note: in instantiation of function template specialization 'std::visit<visitor, const Expr &>' requested here
return std::visit(visitor{}, expr);
^
In file included from main.cpp:6:
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:821:36: error: fields must have a constant size: 'variable length array in structure' extension will never be supported
_Multi_array<_Tp, __rest...> _M_arr[__first + __do_cookie];
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1016:36: note: in instantiation of template class 'std::__detail::__variant::_Multi_array<int (*)(visitor &&, const Expr &), 4>' requested here
static constexpr _Array_type _S_vtable
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1638:55: note: in instantiation of template class 'std::__detail::__variant::__gen_vtable<true, int, visitor &&, const Expr &>' requested here
constexpr auto& __vtable = __detail::__variant::__gen_vtable<
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1654:14: note: in instantiation of function template specialization 'std::__do_visit<false, true, visitor, const Expr &>' requested here
return __do_visit(std::forward<_Visitor>(__visitor),
^
main.cpp:48:15: note: in instantiation of function template specialization 'std::visit<visitor, const Expr &>' requested here
return std::visit(visitor{}, expr);
^
In file included from main.cpp:6:
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1016:36: error: constexpr variable '_S_vtable' must be initialized by a constant expression
static constexpr _Array_type _S_vtable
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1640:45: note: in instantiation of static data member 'std::__detail::__variant::__gen_vtable<true, int, visitor &&, const Expr &>::_S_vtable' requested here
_Result_type, _Visitor&&, _Variants&&...>::_S_vtable;
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1654:14: note: in instantiation of function template specialization 'std::__do_visit<false, true, visitor, const Expr &>' requested here
return __do_visit(std::forward<_Visitor>(__visitor),
^
main.cpp:48:15: note: in instantiation of function template specialization 'std::visit<visitor, const Expr &>' requested here
return std::visit(visitor{}, expr);
^
4 errors generated.
How to make the code above to compile?
Basically, the question is how to have recursive types with std::variant and std::shared_ptr for incomplete types.
The command clang++ --version gives:
clang version 9.0.0-2 (tags/RELEASE_900/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Related
I am confused on how to use this method. I tried the following:
std::equal_to(T objA, T objB);
I get errors if I use it like this. However, I have seen countless examples that use it like this:
pairs1 = mismatch(v1.begin(), v1.end(),
v2.begin(),
equal_to<int>());
How is this method supposed to be used?
Sample Error:
prog.cpp: In function ‘int main()’:
prog.cpp:25:31: error: no matching function for call to ‘std::equal_to<int>::equal_to(std::vector<int>&, std::vector<int>&)’
pairs1 = equal_to<int>(v2, v1));
^
In file included from /usr/include/c++/5/string:48:0,
from /usr/include/c++/5/random:40,
from /usr/include/c++/5/bits/stl_algo.h:66,
from /usr/include/c++/5/algorithm:62,
from prog.cpp:2:
/usr/include/c++/5/bits/stl_function.h:352:12: note: candidate: constexpr std::equal_to<int>::equal_to()
struct equal_to : public binary_function<_Tp, _Tp, bool>
^
/usr/include/c++/5/bits/stl_function.h:352:12: note: candidate expects 0 arguments, 2 provided
/usr/include/c++/5/bits/stl_function.h:352:12: note: candidate: constexpr std::equal_to<int>::equal_to(const std::equal_to<int>&)
/usr/include/c++/5/bits/stl_function.h:352:12: note: candidate expects 1 argument, 2 provided
/usr/include/c++/5/bits/stl_function.h:352:12: note: candidate: constexpr std::equal_to<int>::equal_to(std::equal_to<int>&&)
/usr/include/c++/5/bits/stl_function.h:352:12: note: candidate expects 1 argument, 2 provided
equal_to is not a function, you cannot call it directly like one. It's a class that has the operator() defined which means that you need to create an object and call operator() on that object.
Possible implementation:
template <class T = void>
struct equal_to {
constexpr bool operator()(const T& lhs, const T& rhs) const
{
return lhs == rhs;
}
};
Usage example:
auto test()
{
auto my_comp = equal_to<int>{};
bool b1 = my_comp(2, 3);
// or create a temporary object and call it in one line:
bool b2 = equal_to<int>{}(2, 3);
}
It also has a specialization that will deduce the arguments passed to operator():
template <>
struct equal_to<void> {
template <class T, class U>
constexpr auto operator()(T&& lhs, U&& rhs) const
-> decltype(std::forward<T>(lhs) == std::forward<U>(rhs))
{
return std::forward<T>(lhs) == std::forward<U>(rhs);
}
};
auto test2()
{
using namespace std::string_literals;
auto my_comp = equal_to<>{};
bool b1 = my_comp(2, 3);
bool b2 = my_comp("one string"s, "another string"s);
}
How can we make a template accept only basic data types.
template <typename T>
void GetMaxValue( T& x )
{
//... Finds max Value
}
In the above function GetMaxValue we are able to pass any value without any an error.
But the std Function std::numeric_limits<T>::max() has handled it.
For example:
auto max = std::numeric_limits< std::map<int,int> >::max();
will Give an error error C2440: '<function-style-cast>' : cannot convert from 'int' to 'std::map<_Kty,_Ty>'
With constraints in C++20:
#include <type_traits>
template <class T>
requires std::is_arithmetic_v<T>
void GetMaxValue( T& x )
{
//... Finds max Value
}
Usage:
int a = 0;
GetMaxValue(a); // fine
std::vector<int> b;
GetMaxValue(b); // compiler error
Demo
With std::enable_if otherwise:
template <class T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
void GetMaxValue( T& x )
{
//... Finds max Value
}
Demo 2
The error messages pre-constraints are harder to read:
error: no matching function for call to 'GetMaxValue(std::vector<int>&)'
| GetMaxValue(b); // compiler error
| ^
Note: candidate: 'template<class T, typename std::enable_if<is_arithmetic_v<T>, int>::type <anonymous> > void GetMaxValue(T&)'
| void GetMaxValue( T& x )
| ^~~~~~~~~~~
note: template argument deduction/substitution failed:
error: no type named 'type' in 'struct std::enable_if<false, int>'
| template <class T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
| ^
In instantiation of 'void GetMaxValue(T&) [with T = int; typename std::enable_if<is_arithmetic_v<T>, int>::type <anonymous> = 0]'
vs
error: cannot call function 'void GetMaxValue(T&) [with T = std::vector<int>]'
| GetMaxValue(b); // compiler error
| ^
note: constraints not satisfied
In function 'void GetMaxValue(T&) [with T = std::vector<int>]':
required by the constraints of 'template<class T> requires is_arithmetic_v<T> void GetMaxValue(T&)'
note: the expression 'is_arithmetic_v<T>' evaluated to 'false'
| requires std::is_arithmetic_v<T>
| ~~~~~^~~~~~~~~~~~~~~~~~
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;
I'm trying to use a template class (here Foo), with a basic type like:
hana::tuple<hana::pair<hana::type<int>, Runtime>> with Runtime a class which obiviously can't be constepxr.
But the type can be construct in several way that's why I use:
hana::tuple<hana::pair<hana::type<int>, hana::type<Runtime>>> to do the work at compile time.
So the question is basically how convert from the first tuple type to the second one. I wonder if there is something in hana that could help me. Or even better, some tips on that kind of "conversion".
namespace hana = boost::hana;
using namespace hana::literals;
struct Runtime { std::vector<int> data; };
template < typename T >
struct Foo {
T data;
};
constexpr decltype(auto) convertMap(auto storageMap) {
return hana::make_type(hana::transform(
storageMap,
[] (auto pair) {
return hana::make_pair(
hana::first(pair),
typename decltype(hana::typeid_(hana::second(pair)))::type {});
}));
}
int main() {
constexpr auto map = hana::make_tuple(
hana::make_pair(hana::type_c<int>, hana::type_c<Runtime>)
);
constexpr auto result = convertMap(map);
static_assert(result ==
hana::type_c<hana::tuple<hana::pair<hana::type<int>, Runtime>>>);
Foo<typename decltype(result)::type> test;
}
As you can see I tried some c++1z convertMap with hana::transform and lambdas, but the second tuple can't be constexpr, so I can't pass it to hana::make_type hoping to get a hana::type_c.
test.cpp: In function ‘int main()’:
test.cpp:70:41: error: ‘constexpr decltype(auto) convertMap(auto:27) [with auto:27 = boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, boost::hana::type_impl<Runtime>::_> >]’ called in a constant expression
constexpr auto result = convertMap(map);
^
test.cpp:53:27: note: ‘constexpr decltype(auto) convertMap(auto:27) [with auto:27 = boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, boost::hana::type_impl<Runtime>::_> >]’ is not usable as a constexpr function because:
constexpr decltype(auto) convertMap(auto storageMap) {
^~~~~~~~~~
test.cpp:53:27: error: temporary of non-literal type ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ in a constant expression
In file included from /usr/include/boost/hana/detail/struct_macros.hpp:29:0,
from /usr/include/boost/hana/adapt_adt.hpp:15,
from lib/hana/include/boost/hana.hpp:59,
from test.cpp:1:
/usr/include/boost/hana/tuple.hpp:68:12: note: ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ is not literal because:
struct tuple
^~~~~
/usr/include/boost/hana/tuple.hpp:68:12: note: ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ has a non-trivial destructor
All you care about is a type - you can therefore hide the computation of the type in a non-constexpr implementation function, and afterwards call it with decltype(...){} to "force constexpr":
template <typename T>
decltype(auto) convertMapImpl(T storageMap)
{
return hana::make_type(hana::transform(storageMap, [](auto pair) {
return hana::make_pair(hana::first(pair),
typename decltype(hana::typeid_(hana::second(pair)))::type{});
}));
}
template <typename T>
constexpr decltype(auto) convertMap(T storageMap)
{
return decltype(convertMapImpl(storageMap)){};
}
live wandbox example
Also note that using auto in a function signature is a gcc extension - you should use a template parameter instead to be standard-compliant.
I have an enum like this:
namespace api {
enum Operation {
INSERT =1,
UPDATE =2,
DELETE =3,
};
const Operation Operation_MIN = INSERT;
const Operation Operation_MAX = DELETE;
} // api
and I want to write some tests that use it:
#include "gtest.h"
class FWTest : public ::testing::Test {};
class FWTestPar : public FWTest, public ::testing::WithParamInterface<api::Operation>
{};
INSTANTIATE_TEST_CASE_P(Piping, FWTestPar, ::testing::Range(
api::Operation_MIN, api::Operation_MAX+1)
);
To be able to increment enums, I wrote this operator:
template <typename E>
constexpr typename std::enable_if<std::is_enum<E>::value, E>::type
operator+(E val, int step)
{
return static_cast<E>(static_cast<int>(val) + step);
}
This compiles fine with G++ from 4.8 to 6.2, but clang refuses to find operator+ :
clang++ -g -std=c++14 -Wall -pedantic -Wextra -Wformat=2 -I ~/workspace/zarquon -o "enum_inc_real" "enum_inc_real.cc" (in directory: /tmp)
In file included from enum_inc_real.cc:1:
third_party/gmock/gtest/gtest.h:10250:34: error: assigning to 'api::Operation' from incompatible type 'int'
for (T i = begin; i < end; i = i + step)
^ ~~~~~~~~
third_party/gmock/gtest/gtest.h:10191:33: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::CalculateEndIndex' requested here
step_(step), end_index_(CalculateEndIndex(begin, end, step)) {}
^
third_party/gmock/gtest/gtest.h:15815:11: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::RangeGenerator' requested here
new internal::RangeGenerator<T, IncrementT>(start, end, step));
^
third_party/gmock/gtest/gtest.h:15820:10: note: in instantiation of function template specialization 'testing::Range<api::Operation, int>' requested here
return Range(start, end, 1);
^
enum_inc_real.cc:34:55: note: in instantiation of function template specialization 'testing::Range<api::Operation>' requested here
INSTANTIATE_TEST_CASE_P(Piping, FWTestPar, ::testing::Range(
^
In file included from enum_inc_real.cc:1:
third_party/gmock/gtest/gtest.h:10213:14: error: assigning to 'api::Operation' from incompatible type 'int'
value_ = value_ + step_;
^ ~~~~~~~~~~~~~~
third_party/gmock/gtest/gtest.h:10204:5: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::Iterator::Advance' requested here
Iterator(const ParamGeneratorInterface<T>* base, T value, int index,
^
third_party/gmock/gtest/gtest.h:10195:16: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::Iterator::Iterator' requested here
return new Iterator(this, begin_, 0, step_);
^
third_party/gmock/gtest/gtest.h:10189:3: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::Begin' requested here
RangeGenerator(T begin, T end, IncrementT step)
^
third_party/gmock/gtest/gtest.h:15815:11: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::RangeGenerator' requested here
new internal::RangeGenerator<T, IncrementT>(start, end, step));
^
third_party/gmock/gtest/gtest.h:15820:10: note: in instantiation of function template specialization 'testing::Range<api::Operation, int>' requested here
return Range(start, end, 1);
^
enum_inc_real.cc:34:55: note: in instantiation of function template specialization 'testing::Range<api::Operation>' requested here
INSTANTIATE_TEST_CASE_P(Piping, FWTestPar, ::testing::Range(
^
If the templated operator+ is defined inside the api namespace, then clang++ also accepts this code.
So, which compiler is right?
Do I really have to drag operator+ into every single namespace that contains an enum I want to increment?
The odd thing is that gtest does something similar to this:
namespace testin_ {
namespace intl {
template<typename T>
class PII {};
template<typename T, typename Inc>
class RG{
public:
RG(T begin, T end, Inc s)
: begin_{begin}, end_{end}, step_{s}, end_index_{CalculateEndIndex(begin, end, s)}
{}
T begin() { return begin_; }
T end () { return end_; }
private:
class It : public PII<T> {
void advance() {
value_ = value_ + step_;
}
T value_;
int index_;
Inc step_;
};
static int CalculateEndIndex(const T& begin,
const T& end,
const Inc& step) {
int end_index = 0;
for (T i = begin; i < end; i = i + step)
end_index++;
return end_index;
}
const T begin_;
const T end_;
const Inc step_;
const int end_index_;
};
} // intl
template<typename T, typename Inc>
intl::RG<T, Inc> Rg(T start, T end, Inc step) {
return intl::RG<T, Inc>(start, end, step);
}
}
auto gen = testin_::Rg(api::INSERT, api::DELETE, 1);
bu that is accepted without complaints by clang++