I'm building a custom cMessage that includes vectors as some fields. I have no issue with int or double vectors, but with string vectors, I get an error. Below is a sample message definition to reproduce the issue.
cplusplus {{
#include <vector>
typedef std::vector<int> IntVector;
typedef std::vector<string> StrVector;
}};
class IntVector { #existingClass; };
class StrVector { #existingClass; };
message sampleMessage extends cMessage
{
IntVector SampleIntVector;
StrVector SampleStrVector;
}
In my code, I have the below block
sampleMessage *msg = new sampleMessage();
vector<int> intVect = {1,2};
vector<string> stringVect;
string inputString = "dummy string";
stringVect.push_back(inputString);
msg->setSampleIntVector(intVect);
msg->setSampleStrVector(stringVect);
Using OMNeT++ version 6.0 pre10, at the 7th line, I get the error below suggesting the cMessage is expecting a vector<char *>
error: no viable conversion from 'vector<std::string>' to 'const vector<char *>'
I also tried on OMNeT++ version 5.6.2 and got a different error message. For clarity, the file testModel_m.cc is generated by OMNeT++.
testModel_m.cc:170:13: error: use of overloaded operator '<<' is ambiguous (with operand types 'std::ostream' (aka 'basic_ostream<char>') and 'const std::__cxx11::basic_string<char>')
out << *it;
~~~ ^ ~~~
testModel_m.cc:2040:45: note: in instantiation of function template specialization 'operator<<<std::__cxx11::basic_string<char>, std::allocator<std::__cxx11::basic_string<char> > >' requested here
case 1: {std::stringstream out; out << pp->getSampleStrVector(); return out.str();}
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/basic_string.h:6416:5: note: candidate function [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]
operator<<(basic_ostream<_CharT, _Traits>& __os,
^
testModel_m.cc:158:22: note: candidate function [with T = std::__cxx11::basic_string<char>]
inline std::ostream& operator<<(std::ostream& out,const T&) {return out;}
If I change the vector to char *, it works, but for my use case, I would need a vector of string since I search for values in a vector and with char *, that doesn't work quite well.
Is there a way to have a vector<string> field as part of the custom cMessage?
The workaround for the error use of overloaded operator '<<' is ambiguous is adding own definition of operator<< inside cplusplus {{ }} block, for example:
cplusplus {{
#include <vector>
typedef std::vector<int> IntVector;
typedef std::vector<std::string> StrVector;
std::ostream& operator<<(std::ostream &os, const StrVector& vec) {
std::stringstream out;
for (auto i : vec) {
out << i << ", ";
}
return os << out.str();
};
}};
class IntVector { #existingClass; };
class StrVector { #existingClass; };
message sampleMessage {
IntVector SampleIntVector;
StrVector SampleStrVector;
}
Related
I have seen an idiom for using Derived type traits in the base class of a CRTP pattern that looks like this:
template<typename Derived>
struct traits;
template<typename Derived>
struct Base {
using size_type = typename traits<Derived>::size_type;
};
template <typename T>
struct Derived1 : Base<Derived1<T>>{
using size_type = size_t;
void print(){ std::cout << "Derived1" << std::endl; }
};
template <typename T>
struct traits<Derived1<T>> {
using size_type = size_t;
};
int main()
{
using T = float;
Derived1<T> d1;
d1.print();
}
My understanding is that the purpose of the idiom is to delay the instantiation of the Base class's size_type. What I am confused by is the fact that this pattern only seems to work if the derived class is itself templated. For instance, if we change the code to:
template<typename Derived>
struct traits;
template<typename Derived>
struct Base {
using size_type = typename traits<Derived>::size_type;
};
struct Derived1 : Base<Derived1>{
using size_type = size_t;
void print(){ std::cout << "Derived1" << std::endl; }
};
template <>
struct traits<Derived1> {
using size_type = size_t;
};
int main()
{
Derived1 d1;
d1.print();
}
then we get the error
prog.cc: In instantiation of 'struct Base<Derived1>':
prog.cc:21:19: required from here
prog.cc:18:58: error: invalid use of incomplete type 'struct traits<Derived1>'
using size_type = typename traits<Derived>::size_type;
^
prog.cc:14:8: note: declaration of 'struct traits<Derived1>'
struct traits;
^~~~~~
prog.cc: In function 'int main()':
prog.cc:33:9: error: 'Derived1' is not a template
Derived1<float> d1;
Could somebody give me an explanation indicating why the templated derived class compiles, but the untemplated class does not?
The issue you're seeing has nothing to do with CRTP.
Here's what the standard mentions.
If a class template has been declared, but not defined, at the point of instantiation (13.7.4.1),
the instantiation yields an incomplete class type (6.7). [Example:
template<class T> class X; X<char> ch; // error: incomplete type
X<char>
Your traits has only been declared at the point of instantiation of Base<Derived>, hence as per the standard(see above extraction from the standard), struct traits<Derived> yields an incomplete type.
You should reorder the code so that it sees the traits<Derived> specialization when Base<Derived> gets instantiated.
The compilation error you are seeing has nothing to do with CRTP, it's just a bit of a mish-mash of dependencies.
In the code without the templation, your "Base" struct needs the definition of the specialized "traits" struct but it only appears afterwards, so it tries to use the incomplete type it saw in the declaration above.
To get the code to work you need to have the "traits" specialization before the Base declaration, which requires you to also add a declaration of Derived 1, here is a compiling code:
class Derived1;
template<typename Derived>
struct traits;
template <>
struct traits<Derived1> {
using size_type = size_t;
};
template<typename Derived>
struct Base {
using size_type = typename traits<Derived>::size_type;
};
struct Derived1 : Base<Derived1>{
using size_type = size_t;
void print(){ std::cout << "Derived1" << std::endl; }
};
int main()
{
Derived1 d1;
d1.print();
}
I'm not sure if this is related to Error when adapting a class with BOOST_FUSION_ADAPT_ADT ,but even if it is, the question behind it is still not answered/still fails see the comment of the author Error when adapting a class with BOOST_FUSION_ADAPT_ADT . (I used boost 1.69)
I have a struct
#include <string>
#include <cstdint>
namespace rtsp {
struct request {
};
struct response {
uint_fast16_t rtsp_version_major;
uint_fast16_t rtsp_version_minor;
uint_fast16_t status_code;
std::string reason_phrase;
};
using message = boost::variant<request, response>;
}
Which I was able to use with boost::spirit::qi via
BOOST_FUSION_ADAPT_STRUCT(
rtsp::response,
(uint_fast16_t, rtsp_version_major)
(uint_fast16_t, rtsp_version_minor)
(uint_fast16_t, status_code)
(std::string, reason_phrase)
but since I now also want to generate and not only parse the struct it, using boost::spirit::karma I have to use
BOOST_FUSION_ADAPT_ADT(
rtsp::response,
(uint_fast16_t,const uint_fast16_t&,obj.rtsp_version_major, obj.rtsp_version_major = val)
(uint_fast16_t,const uint_fast16_t&,obj.rtsp_version_minor, obj.rtsp_version_minor = val)
(uint_fast16_t,const uint_fast16_t&,obj.status_code, obj.status_code = val)
(std::string,const std::string&,obj.reason_phrase, obj.reason_phrase = val)
)
But it explodes with several errors, from template errors up to syntax errors, for example with
In file included from
/Users/markus/Entwicklung/HTW/RTSP-Streaming/src/streaming_lib/include/rtsp_request.hpp:11:
In file included from
/Users/markus/include/boost/spirit/include/qi.hpp:16: In file included
from /Users/markus/include/boost/spirit/home/qi.hpp:14: In file
included from
/Users/markus/include/boost/spirit/home/qi/action.hpp:14: In file
included from
/Users/markus/include/boost/spirit/home/qi/action/action.hpp:16:
/Users/markus/include/boost/spirit/home/qi/detail/attributes.hpp:153:9:
error: ambiguous partial specializations of
'transform_attribute, unsigned short, boost::spirit::qi::domain, void>'
: transform_attribute
Since it already fails at the #include <boost/spirit/include/qi.hpp> line, maybe someone knows how to correct the BOOST_FUSION_ADAPT_ADT phrase?
Whole code:
/*! #file rtsp_request.hpp
*
*/
#ifndef RTSP_GUI_RTSP_PARSER_HPP
#define RTSP_GUI_RTSP_PARSER_HPP
#include <string>
#include <cstdint>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/adapt_adt.hpp>
#include <boost/fusion/adapted/adt/adapt_adt.hpp>
#include <boost/spirit/include/support_adapt_adt_attributes.hpp>
namespace rtsp {
struct request {
};
struct response {
uint_fast16_t rtsp_version_major;
uint_fast16_t rtsp_version_minor;
uint_fast16_t status_code;
std::string reason_phrase;
};
using message = boost::variant<request, response>;
}
BOOST_FUSION_ADAPT_ADT(
rtsp::response,
(uint_fast16_t,const uint_fast16_t&,obj.rtsp_version_major, obj.rtsp_version_major = val)
(uint_fast16_t,const uint_fast16_t&,obj.rtsp_version_minor, obj.rtsp_version_minor = val)
(uint_fast16_t,const uint_fast16_t&,obj.status_code, obj.status_code = val)
(std::string,const std::string&,obj.reason_phrase, obj.reason_phrase = val)
)
/*
BOOST_FUSION_ADAPT_STRUCT(
rtsp::response,
(uint_fast16_t, rtsp_version_major)
(uint_fast16_t, rtsp_version_minor)
(uint_fast16_t, status_code)
(std::string, reason_phrase)
)*/
namespace rtsp {
template<typename Iterator>
struct rtsp_response_grammar
: ::boost::spirit::qi::grammar<Iterator, response()> {
rtsp_response_grammar() : rtsp_response_grammar::base_type(start) {
namespace ns = ::boost::spirit::standard;
using ::boost::spirit::qi::uint_parser;
using ::boost::spirit::qi::lexeme;
using ::boost::spirit::qi::lit;
using boost::spirit::qi::omit;
using ::boost::spirit::qi::repeat;
quoted_string %= lexeme['"' >> +(ns::char_ - '"') >> '"'];
status_code = uint_parser<uint_fast16_t, 10, 3, 3>();
at_least_one_digit = uint_parser<uint_fast16_t, 10, 1>();
start %= lit("RTSP/") >> at_least_one_digit >> "." >> at_least_one_digit >> omit[+ns::space]
>> status_code >> omit[+ns::space]
>> *(ns::char_ - (lit("\r") | lit("\n")))
>> lit("\r\n");
}
boost::spirit::qi::rule<Iterator, std::string()> quoted_string;
boost::spirit::qi::rule<Iterator, uint_fast16_t()> status_code;
boost::spirit::qi::rule<Iterator, uint_fast16_t()> at_least_one_digit;
boost::spirit::qi::rule<Iterator, response()> start;
};
template <typename OutputIterator>
bool generate_response(OutputIterator sink, const response& reponse)
{
using ::boost::spirit::karma::lit;
return boost::spirit::karma::generate(sink, lit("RTSP/"), reponse);
}
}
#endif //RTSP_GUI_RTSP_PARSER_HPP
/*! #file rtsp_request.cpp
*
*/
#include "rtsp_request.hpp"
#include <iterator>
template
struct rtsp::rtsp_response_grammar<std::string::const_iterator>;
template
bool rtsp::generate_response<std::back_insert_iterator<std::string>>(std::back_insert_iterator<std::string> sink, const response& reponse);
BTW, trying to just use the BOOST_FUSION_ADAPT_STRUCT with the karma generator function would also fail with:
In file included from
/Users/markus/Entwicklung/HTW/RTSP-Streaming/src/streaming_lib/src/rtsp_request.cpp:5:
In file included from
/Users/markus/Entwicklung/HTW/RTSP-Streaming/src/streaming_lib/include/rtsp_request.hpp:11:
In file included from
/Users/markus/include/boost/spirit/include/qi.hpp:16: In file included
from /Users/markus/include/boost/spirit/home/qi.hpp:14: In file
included from
/Users/markus/include/boost/spirit/home/qi/action.hpp:14: In file
included from
/Users/markus/include/boost/spirit/home/qi/action/action.hpp:14: In
file included from
/Users/markus/include/boost/spirit/home/qi/meta_compiler.hpp:15: In
file included from
/Users/markus/include/boost/spirit/home/qi/domain.hpp:18: In file
included from
/Users/markus/include/boost/spirit/home/support/context.hpp:18: In
file included from
/Users/markus/include/boost/spirit/home/support/nonterminal/expand_arg.hpp:20:
/Users/markus/include/boost/spirit/home/support/string_traits.hpp:156:26:
error: implicit instantiation of undefined template
'boost::spirit::traits::char_type_of'
typedef typename char_type_of::type char_type;
^ /Users/markus/include/boost/spirit/home/support/string_traits.hpp:242:14:
note: in instantiation of template class
'boost::spirit::traits::extract_c_string' requested
here
typename extract_c_string::char_type const*
^ /Users/markus/include/boost/spirit/home/karma/string/lit.hpp:180:21:
note: while substituting deduced template arguments into function
template 'get_c_string' [with String = rtsp::response]
get_c_string(
^ /Users/markus/include/boost/spirit/home/karma/generate.hpp:69:45:
note: in instantiation of function template specialization
'boost::spirit::karma::literal_string::generate
, mpl_::int_<0>, boost::spirit::unused_type>, boost::spirit::context, boost::spirit::locals >, boost::spirit::unused_type, rtsp::response>' requested
here
return compile(expr).generate(sink, context, unused, attr);
^ /Users/markus/include/boost/spirit/home/karma/generate.hpp:91:23:
note: in instantiation of function template specialization
'boost::spirit::karma::generate
, mpl_::int_<0>, boost::proto::exprns_::expr > >, 0>, rtsp::response>'
requested here
return karma::generate(sink, expr, attr);
^ /Users/markus/Entwicklung/HTW/RTSP-Streaming/src/streaming_lib/include/rtsp_request.hpp:69:38:
note: in instantiation of function template specialization
'boost::spirit::karma::generate
, boost::proto::exprns_::expr > >, 0>, rtsp::response>'
requested here
return boost::spirit::karma::generate(sink, lit("RTSP/"), reponse);
^ /Users/markus/include/boost/spirit/home/support/string_traits.hpp:96:12:
note: template is declared here
struct char_type_of;
^ /Users/markus/include/boost/spirit/home/support/string_traits.hpp:179:20:
error: no matching function for call to 'call'
return extract_c_string::call(str);
^~~~~~~~~~~~~~~~~~~~~~~~~ /Users/markus/include/boost/spirit/home/support/string_traits.hpp:238:42:
note: in instantiation of member function
'boost::spirit::traits::extract_c_string::call'
requested here
return extract_c_string::call(str);
^ /Users/markus/include/boost/spirit/home/karma/string/lit.hpp:180:21:
note: in instantiation of function template specialization
'boost::spirit::traits::get_c_string' requested
here
get_c_string(
^ /Users/markus/include/boost/spirit/home/karma/generate.hpp:69:45:
note: in instantiation of function template specialization
'boost::spirit::karma::literal_string::generate
, mpl_::int_<0>, boost::spirit::unused_type>, boost::spirit::context, boost::spirit::locals >, boost::spirit::unused_type, rtsp::response>' requested
here
return compile(expr).generate(sink, context, unused, attr);
^ /Users/markus/include/boost/spirit/home/karma/generate.hpp:91:23:
note: in instantiation of function template specialization
'boost::spirit::karma::generate
, mpl_::int_<0>, boost::proto::exprns_::expr > >, 0>, rtsp::response>'
requested here
return karma::generate(sink, expr, attr);
^ /Users/markus/Entwicklung/HTW/RTSP-Streaming/src/streaming_lib/include/rtsp_request.hpp:69:38:
note: in instantiation of function template specialization
'boost::spirit::karma::generate
, boost::proto::exprns_::expr > >, 0>, rtsp::response>'
requested here
return boost::spirit::karma::generate(sink, lit("RTSP/"), reponse);
^ /Users/markus/include/boost/spirit/home/support/string_traits.hpp:159:25:
note: candidate template ignored: could not match 'T ' against
'rtsp::response'
static T const call (T* str)
^ /Users/markus/include/boost/spirit/home/support/string_traits.hpp:165:25:
note: candidate template ignored: could not match 'const T ' against
'rtsp::response'
static T const call (T const* str)
A well defined grammar handles either BOOST_FUSION_ADAPT_STRUCT or BOOST_FUSION_ADAPT_ADT without problems.
Example:
namespace rtsp {
...
template <typename OutputIterator>
bool generate_response(OutputIterator sink, const response& reponse)
{
using ::boost::spirit::karma::lit;
using ::boost::spirit::karma::uint_;
using ::boost::spirit::karma::string;
return boost::spirit::karma::generate(sink,
lit("RTSP/") << uint_ << "." << uint_ << " " << uint_ << " " << string
, reponse);
}
}
int main()
{
std::string s;
generate_response(std::back_inserter(s), rtsp::response{ 1, 0, 200, "OK" });
std::cout << s;
}
Prints: RTSP/1.0 200 OK
Update: Qi bug involving BOOST_FUSION_ADAPT_ADT confirmed and reported.
I want to use a custom allocator to allocate memory from a freelist for std::basic_ostringstream. Here is my custom allocator which I want to use:
template <class Tp>
struct NAlloc {
typedef Tp value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
NAlloc() = default;
template <class T> NAlloc(const NAlloc<T>&) {}
Tp* allocate(std::size_t n) {
n *= sizeof(Tp);
memoryPool *memPool = memoryPool::GetInstance(10);//get memory pool instance
std::cout << "allocating " << n << " bytes\n";
return static_cast<Tp*>(memPool->allocate(n)); //get memory from pool
}
void deallocate(Tp* p, std::size_t n) {
std::cout << "deallocating " << n*sizeof*p << " bytes\n";
memoryPool *memPool = memoryPool::GetInstance(10);
memPool->deallocate(static_cast<void*>(p));//return memory to pool
}
template<typename U>
struct rebind {
typedef NAlloc<U> other;
};
Then, I use it like this:
typedef std::basic_string<char, std::char_traits<char>, NAlloc<char>> OstringStream;
****Problem:****
int main()
{
OstringStream os; //Object creation
os << " Hello, this is OstreamStream class with memory pool"; //here I am getting error
}
Error: 'OstringStream {aka std::basic_string<char, std::char_traits<char>, NAlloc<char> >}' is not derived from 'std::basic_ostream<_CharT, _Traits>'
Your OstringStream type is a typedef of std::basic_string, not of std::basic_ostream. That is why you are getting the error from operator<<. The left-hand operand must be an object derived from std::basic_ostream, exactly as the error message is saying.
std::basic_ostream itself does not use an allocator at all. It uses std::basic_streambuf for all of its I/O. For instance, std::ostringstream uses std::stringbuf, which uses the default std::allocator.
In C++11, std::basic_ostringstream has an optional Allocator template parameter, which it passes down to its internal std::basic_stringbuf. So, you could write your typedef like this instead:
typedef std::basic_ostringstream<char, std::char_traits<char>, NAlloc<char>> OstringStream;
int main()
{
OstringStream os;
os << " Hello, this is OstringStream with memory pool";
}
In earlier C++ versions, you would have to:
define a typedef of std::basic_stringbuf that uses your custom allocator instead of the default allocator.
construct a standard std::ostream object that uses an instance of your custom stringbuf type.
For example:
typedef std::basic_stringbuf<char, std::char_traits<char>, NAlloc<char> > Stringbuf_NAlloc;
class OstringStream : public Stringbuf_NAlloc, public std::ostream
{
public:
OstringStream() : Stringbuf_NAlloc(std::ios_base::out), std::ostream(this) {}
};
int main()
{
OstringStream os;
os << " Hello, this is OstringStream with memory pool";
}
In either case, know that the os.str() method will no longer return a standard std::string, which uses the default allocator. It will return a std::basic_string that uses your custom allocator instead. That will cause problems when trying to assign the return value of os.str() to a standard std::string, eg:
std::string s = os.str(); // error!
error: conversion from ‘std::__cxx11::basic_ostringstream<char, std::char_traits<char>, NAlloc>::__string_type {aka std::__cxx11::basic_string<char, std::char_traits<char>, NAlloc>}’ to non-scalar type ‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’ requested
So be aware of that. The STL is not very flexible when it comes to mixing allocators, so if you use a custom allocator, you usually have to apply it to every type of data container you use from the STL.
Based on the answer in Detecting constexpr with SFINAE I'm trying to use SFINAE to check if a 'constexpr' is present in my class.
The problem is that the constexpr is a function pointer:
#include <type_traits>
#include <iostream>
typedef int (*ptr_t)();
int bar() { return 9; }
struct Foo {
static constexpr ptr_t ptr = &bar;
};
namespace detail {
template <ptr_t>
struct sfinae_true : std::true_type {};
template <class T>
sfinae_true<T::ptr> check(int);
// Commented out to see why clang was not evaluating to true. This should only be
// a comment when debugging!
// template <class>
// std::false_type check(...);
} // detail::
template <class T>
struct has_constexpr_f : decltype(detail::check<T>(0)) {};
int main(int argc, char *argv[]) {
std::cout << has_constexpr_f<Foo>::value << std::endl;
return 0;
}
It seems to work fine using gcc, but clang complains:
test.cxx:23:39: error: no matching function for call to 'check'
struct has_constexpr_f : decltype(detail::check<T>(0)) {};
^~~~~~~~~~~~~~~~
test.cxx:26:22: note: in instantiation of template class 'has_constexpr_f<Foo>' requested here
std::cout << has_constexpr_f<Foo>::value << std::endl;
^
test.cxx:16:25: note: candidate template ignored: substitution failure [with T = Foo]: non-type template argument for template parameter of pointer type 'ptr_t' (aka 'int (*)()') must have its address taken
sfinae_true<T::ptr> check(int);
~ ^
1 error generated.
Q1: Can anyone suggest a way of doing this which works both for Clang and GCC?
Q2: Is this a bug in gcc, clang or is this left undefined in the c++ standard?
That's not a bug in clang, but an unfortunate restriction of arguments for non-type template parameters of pointer type (see pointer as non-type template argument). Essentially, you can only use arguments of the form &something: [temp.arg.nontype]/1 (from n3797)
[if the template-parameter is a pointer, its argument can be] a constant expression (5.19) that designates the address of a
complete object with static storage duration and external or
internal linkage or a function with external or internal linkage,
including function templates and function template-ids but excluding
non-static class members, expressed (ignoring parentheses) as &
id-expression, where the id-expression is the name of an object or
function, except that the & may be omitted if the name refers to a
function or array and shall be omitted if the corresponding
template-parameter is a reference; or [..]
[emphasis mine]
You can however, use a function pointer in a constant expression that has a non-pointer type, for example a boolean expression such as
T::ptr != nullptr
This works under clang++3.5 and g++4.8.2:
#include <type_traits>
#include <iostream>
typedef int (*ptr_t)();
int bar() { return 9; }
struct Foo0 {
static constexpr ptr_t ptr = &bar;
};
struct Foo1 {
static const ptr_t ptr;
};
ptr_t const Foo1::ptr = &bar;
struct Foo2 {
static const ptr_t ptr;
};
//ptr_t const Foo2::ptr = nullptr;
namespace detail
{
template <bool>
struct sfinae_true : std::true_type {};
template <class T>
sfinae_true<(T::ptr != nullptr)> check(int);
// the result of the comparison does not care
template <class>
std::false_type check(...);
} // detail::
template <class T>
struct has_constexpr_f : decltype(detail::check<T>(0)) {};
int main(int argc, char *argv[]) {
std::cout << std::boolalpha << has_constexpr_f<Foo0>::value << std::endl;
std::cout << std::boolalpha << has_constexpr_f<Foo1>::value << std::endl;
std::cout << std::boolalpha << has_constexpr_f<Foo2>::value << std::endl;
return 0;
}
Note there's a difference between clang++ and g++ for the second output (Foo1): g++ says true, clang++ says false.
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.