constexpr: saving calculation results [duplicate] - c++

I'm trying to do the following (only relevant parts of code below):
template<typename ContainerType>
struct IsContainerCheck : is_container<ContainerType>
{
static constexpr char* err_value = "Type is not a container model";
};
namespace _check_concept {
template<typename ResultType>
struct run {
constexpr static int apply() {
static_assert(false, IsContainerCheck<ResultType>::err_value)
return 0;
}
};
template<>
struct run<true_t> {
constexpr static int apply() {
return 0;
}
};
}
This fails because the static_assert allows only literals to be printed. The same is with BOOST_STATIC_ASSERT_MSG macro.
So my question is - is there any way to output a constexpr string during compilation?
If there is a gcc extension providing this functionality that would also be great.
Used compiler gcc 4.8.1

GCC does not provide such a mechanism as you want. However you will not need
it if you are able to refactor your code somewhat as illustrated in the
following program. (I have filled in a few gaps so as to give us a
compilable example):
#include <type_traits>
#include <vector>
template<typename ContainerType>
struct is_container
{
static bool const value = false;
};
template<>
struct is_container<std::vector<int>>
{
static bool const value = true;
};
template<typename ContainerType>
struct IsContainerCheck // : is_container<ContainerType> <- Uneccessary
{
static_assert(is_container<ContainerType>::value,
"Type is not a container model");
};
namespace _check_concept {
template<typename ResultType>
struct run {
constexpr static int apply() {
return (IsContainerCheck<ResultType>(),0);
}
};
// No such specialization is necessary. Delete it.
// template<>
// struct run<true_t> {
// constexpr static int apply() {
// return 0;
// }
//};
}
using namespace _check_concept;
int main(int argc, char **argv)
{
auto verdict0 = run<std::vector<int>>::apply();
(void)verdict0;
// The following line will static_assert: "Type is not a container model"
auto verdict1 = run<float>::apply();
(void)verdict1;
return 0;
}
In your specialization _check_concept::struct run<true_t> I presume that
true_t is not an alias or equivalent of std::true_type, but rather
just a place-holder for some ResultType that is a container type. As
the test program shows, no such specialization is now necessary, because
IsContainerCheck<ResultType>() will static_assert, or not, depending
on ResultType, in the unspecialized run<ResultType>::apply().

I had some time (and a good liqueur to come along with it) to think more about the problem. This is what I came up with:
namespace _details {
struct PassedCheck {
constexpr static int printError () {
return 0; //no error concept check passed
}
};
template<template<typename> class ConceptCheck, typename ...ModelTypes>
struct check_concept_impl;
template<template<typename> class ConceptCheck, typename FirstType, typename ...ModelTypes>
struct check_concept_impl<ConceptCheck, FirstType, ModelTypes...> : mpl::eval_if< typename ConceptCheck<FirstType>::type,
check_concept_impl<ConceptCheck, ModelTypes...>,
mpl::identity<ConceptCheck<FirstType>>>
{ };
template<template<typename> class ConceptCheck, typename LastType>
struct check_concept_impl<ConceptCheck, LastType> : mpl::eval_if<typename ConceptCheck<LastType>::type,
mpl::identity<PassedCheck>,
mpl::identity<ConceptCheck<LastType>>>
{ };
}
template<template<typename> class ConceptCheck, typename ...ModelTypes>
struct check_concept {
private:
typedef typename _details::check_concept_impl<ConceptCheck, ModelTypes...>::type result_type;
public:
// the constexpr method assert produces shorter, fixed depth (2) error messages than a nesting assert in the trait solution
// the error message is not trahsed with the stack of variadic template recursion
constexpr static int apply() {
return result_type::printError();
}
};
template<typename ContainerType>
struct IsContainerCheck : is_container<ContainerType>
{
template<typename BoolType = false_t>
constexpr static int printError () {
static_assert(BoolType::value, "Type is not a container model");
return 0;
}
};
and the usage:
check_concept<IsContainerCheck, std::vector<int>, std::vector<int>, float, int>::apply();
The solution is probably not the most elegant one but I it keeps the assert message short:
In file included from ../main.cpp:4:0:
../constraint.check.hpp: In instantiation of ‘static constexpr int IsContainerCheck::printError() [with BoolType = std::integral_constant; ContainerType = float]’:
../constraint.check.hpp:61:34: required from ‘static constexpr int check_concept::apply() [with ConceptCheck = IsContainerCheck; ModelTypes = {std::vector >, std::vector >, float, int}]’
../main.cpp:25:83: required from here
../constraint.check.hpp:74:3: error: static assertion failed: Type is not a container model
static_assert(BoolType::value, "Type is not a container model");
The assert is issued in a constexpr method after the check_concept template specialization has been done. Embedding the static assert directly into the template class definition would drag the whole check_concept_impl recursion stack into the error message.
So changing the IsContainerCheck trait to something like (rest of the changes omitted for readibility):
template<typename ContainerType>
struct IsContainerCheck
{
static_assert(is_container<ContainerType>::type::value, "Type is not a container model");
};
would yield an error
../constraint.check.hpp: In instantiation of ‘struct IsContainerCheck’:
../constraint.check.hpp:36:9: required from ‘struct _details::check_concept_impl’
/usr/include/boost/mpl/eval_if.hpp:38:31: required from ‘struct boost::mpl::eval_if, _details::check_concept_impl, boost::mpl::identity > > >’
../constraint.check.hpp:36:9: required from ‘struct _details::check_concept_impl >, float, int>’
/usr/include/boost/mpl/eval_if.hpp:38:31: required from ‘struct boost::mpl::eval_if, _details::check_concept_impl >, float, int>, boost::mpl::identity > > >’
../constraint.check.hpp:36:9: required from ‘struct _details::check_concept_impl >, std::vector >, float, int>’
../constraint.check.hpp:53:84: required from ‘struct check_concept >, std::vector >, float, int>’
../main.cpp:25:81: required from here
../constraint.check.hpp:72:2: error: static assertion failed: Type is not a container model
static_assert(is_container::type::value, "Type is not a container model");
As you can see each recursive eval_if call is emended in the error description which is bad because it makes the error message dependent from the amount and type of template parameters.

Related

SFINAE overload on tuples of different sizes

Consider a struct that contains two types - FirstObjects and SecondObjects, which are both std::tuple<>, e.g. something like:
struct Objects
{
using FirstObjects = std::tuple<int, int>;
using SecondObjects = std::tuple<int>;
};
For convenience we add the following enum:
enum class ObjectCategory
{
FIRST,
SECOND
};
Now consider the following class that is templated on a type such as Objects as described above (the only contract is that it have FirstObjects and SecondObjects and that they be std::tuple):
template <typename T>
class SomeClass
{
public:
using FirstObjects = typename T::FirstObjects;
using SecondObjects = typename T::SecondObjects;
template <std::size_t Idx>
using FirstObject = typename std::tuple_element<Idx, FirstObjects>::type;
template <std::size_t Idx>
using SecondObject = typename std::tuple_element<Idx, SecondObjects>::type;
template <ObjectCategory Category, std::size_t Idx>
using ObjectType = std::conditional_t<Category == ObjectCategory::FIRST, FirstObject<Idx>, SecondObject<Idx>>;
template <ObjectCategory Category, std::size_t Idx>
std::enable_if_t<Category == ObjectCategory::FIRST, const ObjectType<Category, Idx>>& getObject()
{
return std::get<Idx>(firstObjects_);
}
template <ObjectCategory Category, std::size_t Idx>
std::enable_if_t<Category == ObjectCategory::SECOND, const ObjectType<Category, Idx>>& getObject()
{
return std::get<Idx>(secondObjects_);
}
template <ObjectCategory Category, std::size_t Idx>
void doSomething()
{
const ObjectType<Category, Idx>& obj = getObject<Category, Idx>();
}
private:
FirstObjects firstObjects_;
SecondObjects secondObjects_;
};
In brief, SomeClass hosts two member variables, firstObjects_ of type T::FirstObjects, and secondObjects_ of type T::SecondObjects, and has a doSomething() function which makes use of the SFINAE-overloaded getObject() getter method that returns the i-th object contained in firstObjects_ or in secondObjects_ depending on the ObjectCategory chosen.
The problem I have now is:
int main()
{
struct Objects
{
using FirstObjects = std::tuple<int, int>;
using SecondObjects = std::tuple<int>;
};
SomeClass<Objects> object{};
object.doSomething<ObjectCategory::FIRST, 0>(); // <------ works fine
object.doSomething<ObjectCategory::FIRST, 1>(); // <------ compiler error
}
The last line makes the compiler complain:
/usr/include/c++/4.9/tuple: In instantiation of 'struct std::tuple_element<1ul, std::tuple<int> >':
70:114: required by substitution of 'template<class T> template<ObjectCategory Category, long unsigned int Idx> using ObjectType = std::conditional_t<(Category == FIRST), typename std::tuple_element<Idx, typename T::FirstObjects>::type, typename std::tuple_element<Idx, typename T::SecondObjects>::type> [with ObjectCategory Category = (ObjectCategory)0; long unsigned int Idx = 1ul; T = main()::Objects]'
87:42: required from 'void SomeClass<T>::doSomething() [with ObjectCategory Category = (ObjectCategory)0; long unsigned int Idx = 1ul; T = main()::Objects]'
104:50: required from here
/usr/include/c++/4.9/tuple:682:12: error: invalid use of incomplete type 'struct std::tuple_element<0ul, std::tuple<> >'
struct tuple_element<__i, tuple<_Head, _Tail...> >
^
In file included from /usr/include/c++/4.9/tuple:38:0,
from 4:
/usr/include/c++/4.9/utility:85:11: error: declaration of 'struct std::tuple_element<0ul, std::tuple<> >'
class tuple_element;
^
In substitution of 'template<class T> template<ObjectCategory Category, long unsigned int Idx> using ObjectType = std::conditional_t<(Category == FIRST), typename std::tuple_element<Idx, typename T::FirstObjects>::type, typename std::tuple_element<Idx, typename T::SecondObjects>::type> [with ObjectCategory Category = (ObjectCategory)0; long unsigned int Idx = 1ul; T = main()::Objects]':
87:42: required from 'void SomeClass<T>::doSomething() [with ObjectCategory Category = (ObjectCategory)0; long unsigned int Idx = 1ul; T = main()::Objects]'
104:50: required from here
70:114: error: no type named 'type' in 'struct std::tuple_element<1ul, std::tuple<int> >'
It seems like it is not liking the fact that SecondObjects only has one element and therefore std::get<1>(secondObjects_) is not working? But why is this even happening in the first place since I'm calling doSomething on ObjectCategory::FIRST and not ObjectCategory::SECOND?
How can this be solved?
------ EDIT 1 ------
Solution pointed out by #Taekahn is to change the getObject() method as follows:
template <ObjectCategory Category, std::size_t Idx>
const auto& getObject()
{
return std::get<Idx>(firstObjects_);
}
template <ObjectCategory Category, std::size_t Idx, typename = std::enable_if_t<Category == ObjectCategory::SECOND>>
const auto& getObject()
{
return std::get<Idx>(secondObjects_);
}
It seems like the above works because the compiler does not evaluate / check the validity of the return type of a method that has been SFINAE'd out when one uses the keyword auto instead of the explicit type.
But this is only conjecture - would really appreciate a more knowledgeable C++ practitioner to weigh in here!
------ EDIT 2 ------
The above results in ambiguity when trying to do
object.doSomething<ObjectCategory::SECOND, 0>();
The WAR I've found so far is to modify the first getter function to
template <ObjectCategory Category, std::size_t Idx, typename = std::enable_if_t<Category == ObjectCategory::FIRST>, bool = 0 /* dummy param */>
auto& getObject()
{
return std::get<Idx>(firstObjects_);
}
i.e. to add the SFINAE as well as a dummy template arg at the end to allow overloading.

Incomplete type `std::variant<...>` used in nested name specifier

I wrote the following code into a file named main.cpp.
It involves the curiously recurring template pattern (CRTP) with the standard type std::variant.
#include <string>
#include <variant>
#include <vector>
template<typename T>
struct either {
std::vector<T> arg;
};
template<typename T>
struct maybe_either: std::variant<T, either<maybe_either<T>>> {
template<typename U>
maybe_either(U&& v):
std::variant<T, either<maybe_either<T>>>(std::forward<U>(v)) {
}
};
struct var {
std::string name;
};
int main(int, char**) {
auto expression = maybe_either<var>(either<maybe_either<var>>{});
std::visit([&](auto&& v) {
using T = std::decay_t<decltype (v)>;
if constexpr (std::is_same_v<T, var>) {
// ...
} else if constexpr (std::is_same_v<T, either<maybe_either<var>>>) {
// ...
}
}, expression);
return 0;
}
When compiling it with the following command line, I get the error message below:
$ g++ -c -std=c++17 main.cpp
In file included from main.cpp:2:0:
/usr/include/c++/7/variant: In instantiation of ‘constexpr const size_t std::variant_size_v<maybe_either<var> >’:
/usr/include/c++/7/variant:702:10: required from ‘struct std::__detail::__variant::__gen_vtable<void, main(int, char**)::<lambda(auto:1&&)>&&, maybe_either<var>&>’
/usr/include/c++/7/variant:1255:23: required from ‘constexpr decltype(auto) std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = main(int, char**)::<lambda(auto:1&&)>; _Variants = {maybe_either<var>&}]’
main.cpp:32:18: required from here
/usr/include/c++/7/variant:97:29: error: incomplete type ‘std::variant_size<maybe_either<var> >’ used in nested name specifier
inline constexpr size_t variant_size_v = variant_size<_Variant>::value;
^~~~~~~~~~~~~~
/usr/include/c++/7/variant: In instantiation of ‘constexpr const auto std::__detail::__variant::__gen_vtable<void, main(int, char**)::<lambda(auto:1&&)>&&, maybe_either<var>&>::_S_vtable’:
/usr/include/c++/7/variant:711:29: required from ‘struct std::__detail::__variant::__gen_vtable<void, main(int, char**)::<lambda(auto:1&&)>&&, maybe_either<var>&>’
/usr/include/c++/7/variant:1255:23: required from ‘constexpr decltype(auto) std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = main(int, char**)::<lambda(auto:1&&)>; _Variants = {maybe_either<var>&}]’
main.cpp:32:18: required from here
/usr/include/c++/7/variant:711:49: error: ‘_S_apply’ was not declared in this scope
static constexpr auto _S_vtable = _S_apply();
~~~~~~~~^~
My class maybe_either derived from std::variant<...> can be used normally in other contexts, but when I call std::visit(...) on it, it fails to compile. What is wrong?
This is basically LWG3052 which I'm trying to address in P2162.
maybe_either<T> isn't a specialization of std::variant - it inherits from one. And std::visit is currently underspecified. It's not at all clear what kinds of "variants" are allowed to be visited.
libstdc++ implements the original suggested direction in that library issue, which is only specializations of std::variant (of which you are not). libc++, on the other hand, allows types that inherit from std::variant to be visited, so it accepts your example.
The intent is that the example as-is will become well-formed eventually. But until then, you'll have to ensure that the visit you do is directly on a std::variant. You can do so by adding your own member or non-member visit that does this cast for you, so the callers don't have to do it themselves.
For example, this:
template<typename T>
struct maybe_either: std::variant<T, either<maybe_either<T>>> {
using base = typename maybe_either::variant;
template<typename U>
maybe_either(U&& v):
std::variant<T, either<maybe_either<T>>>(std::forward<U>(v)) {
}
template <typename F>
decltype(auto) visit(F&& f) & {
return std::visit(std::forward<F>(f), static_cast<base&>(*this));
}
};
allows this to work:
int main(int, char**) {
auto expression = maybe_either<var>(either<maybe_either<var>>{});
expression.visit([&](auto&& v) {
using T = std::decay_t<decltype (v)>;
if constexpr (std::is_same_v<T, var>) {
// ...
} else if constexpr (std::is_same_v<T, either<maybe_either<var>>>) {
// ...
}
});
return 0;
}

Duplicate type generated from template

I was trying to separate a "generic" map from the event manager implementation, because I need to use it somewhere else. But I ran into something quite unusual for me. So it seems like I'm trying to generate two times (at least) the GetValue function.
#include <tuple>
#include <utility>
#include <vector>
#include <functional>
namespace meta {
template < typename T >
struct CType { using type = T; };
}
namespace containers {
template < template < typename T > class T_Storage,
typename... T_Keys >
class TypedMap {
template < typename T_Key >
using U_Pair = decltype(
std::make_pair(
meta::CType<T_Key>{},
T_Storage<T_Key>{}
));
using U_Map = decltype(
std::make_tuple(
U_Pair<T_Keys>{}
...));
public:
U_Map m_map;
//-----------------------------------------
//! Functions
public:
//!
//! #fn GetValue
//! #brief Acess to map[key]
//! #param type is the key used to find the data
//! #return map[key] reference
//!
template < typename T_Key >
constexpr decltype(auto) GetPair() {
return std::get<U_Pair<T_Key>>(m_map);
}
template < typename T_Key >
decltype(auto) GetValue() {
return std::get<1>(GetPair<T_Key>());
}
};
}
//!
//! #class EventManager
//!
template < typename ... T_Events >
class EventManager {
template < typename T_Event >
using U_EventCallback = std::function<void(T_Event)>;
template < typename T_Event >
using U_ListenersArray = std::vector<U_EventCallback<T_Event>>;
private:
containers::TypedMap<U_ListenersArray> m_listenersMap;
public:
EventManager() = default;
~EventManager() = default;
public:
template < typename T_Event >
decltype(auto) GetListeners() {
return m_listenersMap.template GetValue<T_Event>();
}
};
struct event1 {};
struct event2 {};
using U_EventManager = EventManager<event1,event2>;
int main() {
U_EventManager test;
auto result = test.GetListeners<event1>();
}
Wandbox
UPDATE:
So the error was generated because of a typo... But still I'd like to understand how this error occured, to be able to understand it next time.
In file included from prog.cc:1: /opt/wandbox/clang-head/include/c++/v1/tuple:1018:5: error: static_assert failed due to requirement '!is_same<pair<CType<event1>, vector<function<void (event1)>, allocator<function<void (event1)> > >
>, pair<CType<event1>, vector<function<void (event1)>, allocator<function<void (event1)> > > > >::value' "type not in empty type list"
static_assert(!is_same<_T1, _T1>::value, "type not in empty type list");
^ ~~~~~~~~~~~~~~~~~~~~~~~~~ /opt/wandbox/clang-head/include/c++/v1/tuple:1025:14: note: in instantiation of template class 'std::__1::__find_detail::__find_exactly_one_checked<std::__1::pair<meta::CType<event1>, std::__1::vector<std::__1::function<void (event1)>, std::__1::allocator<std::__1::function<void (event1)> > > >>' requested here
: public __find_detail::__find_exactly_one_checked<_T1, _Args...> {
^ /opt/wandbox/clang-head/include/c++/v1/tuple:1032:23: note: in instantiation of template class 'std::__1::__find_exactly_one_t<std::__1::pair<meta::CType<event1>, std::__1::vector<std::__1::function<void (event1)>, std::__1::allocator<std::__1::function<void (event1)> > > >>' requested here
return _VSTD::get<__find_exactly_one_t<_T1, _Args...>::value>(__tup);
^ prog.cc:45:29: note: in instantiation of function template specialization 'std::__1::get<std::__1::pair<meta::CType<event1>, std::__1::vector<std::__1::function<void (event1)>, std::__1::allocator<std::__1::function<void (event1)> > > >>' requested here
return std::get<U_Pair<T_Key>>(m_map);
^ prog.cc:51:36: note: in instantiation of function template specialization 'containers::TypedMap<U_ListenersArray>::GetPair<event1>' requested here
return std::get<1>(GetPair<T_Key>());
^ prog.cc:79:40: note: in instantiation of function template specialization 'containers::TypedMap<U_ListenersArray>::GetValue<event1>' requested here
return m_listenersMap.template GetValue<T_Event>();
^ prog.cc:92:24: note: in instantiation of function template specialization 'EventManager<event1, event2>::GetListeners<event1>' requested here
auto result = test.GetListeners<event1>();
^ 1 error generated.
You forgot to provide the content for your containers:
template < typename ... T_Events >
class EventManager {
/* ... */
private:
containers::TypedMap<U_ListenersArray> m_listenersMap; // <-- this line, you forgot the type list.
/* ... */
};
The marked line should be:
containers::TypedMap<U_ListenersArray, T_Events...> m_listenersMap;
Not sure... but it seems to me you've forgotten to use T_Events... in EventManager.
I mean... instead of
containers::TypedMap<U_ListenersArray> m_listenersMap;
you should, I suppose, write
containers::TypedMap<U_ListenersArray, T_Events...> m_listenersMap;

templated function which accepts only string or arithmetic

I'm trying to get this to work:
template<class Type>
typename boost::enable_if< boost::mpl::or_<
boost::is_arithmetic<Type>,
is_string<Type> > >::type
get(const std::string &argPath, const Type &argDefault) {
bool caught = false;
std::stringstream ss;
Type value;
try {
value = ptree_.get<Type>(argPath);
} catch(ptree_bad_path &e) {
caught = true;
}
if(caught)
value = argDefault;
ss << value;
parameters_.insert(std::pair<std::string, std::string>(argPath, ss.str()));
return value;
}
I used the following is_string type trait: Type trait for strings
My goal is to restrict my Type to string or arithmetic type so that I can push it to my stringstream.
So this builds, but when I try to use it, it returns the following errors:
error: void value not ignored as it ought to be
In member function ‘typename
boost::enable_if,
is_string, mpl_::bool_, mpl_::bool_,
mpl_::bool_ >, void>::type FooClass::get(const std::string&,
const Type&) [with Type = uint8_t]’
error: return-statement with a value, in function returning 'void'
Here is how I try to use it:
FooClass f;
item_value = f.get("tag1.tag2.item", DEFAULT_ITEM_VALUE);
Any help is appreciated, thanks in advance!
From http://www.boost.org/doc/libs/1_53_0/libs/utility/enable_if.html, enable_if has a second parameter that defaults to void:
template <bool B, class T = void>
struct enable_if_c {
typedef T type;
};
Seems to me you need to include the return type in your enable_if. (It is defaulting to void now.)
template<class Type>
typename boost::enable_if< boost::mpl::or_<
boost::is_arithmetic<Type>,
is_string<Type> >,
Type >::type
get(const std::string &argPath, const Type &argDefault);

Compilation error with Type Traits in boost::type_traits::conditional

I am having a problem in some code using type_traits from boost.
It is quite a complex part of the code, but I could isolate the part that gives the compilation error:
template<const size_t maxLen>
class MyString {
public:
typedef boost::conditional<(maxLen > 0), char[maxLen+1], std::string> ObjExternal;
};
template <class T>
class APIBase {
public:
typedef T obj_type;
typedef typename T::ObjExternal return_type;
};
template <class T>
int edit(const T& field, const typename T::return_type& value)
{
return 0;
}
int myFunction()
{
APIBase<MyString<10> > b;
char c[11];
return edit(b, c);
}
This gives the following error:
test.cpp: In function ‘int myFunction()’:
tes.cpp:109: error: no matching function for call to ‘edit(APIBase >&, char [11])’
tes.cpp:100: note: candidates are: int edit(const T&, const typename T::return_type&) [with T = APIBase >]
However, if I change the line with the code
char c[11];
by
MyString<10>::ObjExternal c;
it works. Similarly, if instead I change the line
typedef boost::conditional<(maxLen > 0), char[maxLen+1], std::string> ObjExternal;
by
typedef char ObjExternal[maxLen+1];
it also works. I am thinking that it is a problem with boost::conditional, as it seems it is not being evaluated right. Is there a problem in my code, or there is an alternative that can be used instead of boost::conditional to have this functionality?
I am thinking about using the 2nd option, but then I could not use maxLen as 0.
You need to use the member typedef type provided by conditional and not the conditional type itself.
Change:
typedef boost::conditional<(maxLen > 0),
char[maxLen+1],
std::string> ObjExternal;
to:
typedef typename boost::conditional<(maxLen > 0),
char[maxLen+1],
std::string>::type ObjExternal;