I'm trying to associate compile time strings to enum values.
Here is my first attempt at the problem:
EnumValue will do the compile time assocation between a string and an enum
template<typename EnumType, int EnumIntValue, const char* EnumStrValue>
class EnumValue
{
public:
static const char* toString()
{
return EnumStrValue;
}
static const int toInt()
{
return EnumIntValue;
}
static EnumType get()
{
return static_cast<EnumType>(EnumIntValue);
}
};
EnumValueHolder will hold the actual values for both string and enum.
I dislike my current design as it still needs to hold a pointer to string. I would prefer a compile time association for this but fail to come up with a more elegant solution
template<typename EnumType>
class EnumValueHolder
{
public:
EnumValueHolder()
{}
EnumValueHolder(const EnumType& value, const char* str)
: value(value), str(str)
{}
bool operator==(const EnumValueHolder<EnumType>& rhs) { return value == rhs.value; }
bool operator==(const EnumType& rhs)const { return value == rhs; }
operator EnumType()const
{
return value;
}
const char* toString()const
{
return str;
}
const int toInt()const
{
return static_cast<int>(value);
}
private:
EnumType value;
char const* str;
};
Marcos to easily refer to enum types and enum value holder construction
#define ENUM_VALUE_TYPE(enumName, enumValue) \
EnumValue<enumName, (int)enumName::enumValue, str_##enumValue>
#define ENUM_VALUE_MAKE(enumName, enumValue) \
EnumValueHolder<enumName> { \
ENUM_VALUE_TYPE(enumName, enumValue)::get(), \
ENUM_VALUE_TYPE(enumName, enumValue)::toString() }
The following are my test cases and usage examples:
const char str_Apple[] = "Apple";
const char str_Orange[] = "Orange";
const char str_Pineapple[] = "Pineapple";
enum class EFruits
{
Apple,
Orange,
Pineapple
};
int main()
{
auto evApple = ENUM_VALUE_MAKE(EFruits, Apple);
std::cout << evApple.toString() << std::endl;
auto evOrange = ENUM_VALUE_MAKE(EFruits, Orange);
std::cout << evOrange.toString() << std::endl;
std::cout << "compare: " << (evApple == evOrange) << std::endl;
evApple = evOrange;
std::cout << evApple.toString() << std::endl;
auto myfruit = ENUM_VALUE_MAKE(EFruits, Pineapple);
std::cout << myfruit.toString() << std::endl;
switch (myfruit)
{
case EFruits::Apple:
std::cout << "Im an apple!" << std::endl;
break;
case EFruits::Orange:
std::cout << "Im an Orange!" << std::endl;
break;
case EFruits::Pineapple:
std::cout << "Im a Pineapple!" << std::endl;
break;
default:break;
}
}
One of the objectives is to remove the global string:
const char str_Apple[] = "Apple";
const char str_Orange[] = "Orange";
const char str_Pineapple[] = "Pineapple";
The other is to create a macro that assoicates an enum with a string
//Some crazy define that makes pairs of enum values and strings as
//compile time constants
#define DEFINE_ENUM_STRING(enumValue)\
enumValue, #enumValue
//Ideally, the macro would be used like this. This should be usable in any
//scope (global, namespace, class)
//with any access specifier (private, protected, public)
enum class EFruits
{
DEFINE_ENUM_STRING(Apple),
DEFINE_ENUM_STRING(Orange),
DEFINE_ENUM_STRING(Pineapple)
};
So there are 2 main questions:
1) Will this current design actually guarantee compile time constants for associating the enum to the string?
2) How can I define a macro to stringify an enum value and declare the value in a enum class using 1 line?
Edit: This should work and compile with msvs2017 on win64 platform using c++ 11.
Thanks.
I think it should work with MSVC2017. It uses C++14 in the constexpr functions but you can split them to single return statement constexprs to be C++11 compatible (however MSVC2017 supports C++14).
EnumConverter stores the char*, the enum and a string hash value for each enum entry. For each enum you must specialize EnumConverter::StrEnumContainer. The enum-string pairs could be generated with a similar macro you specified.
#include <tuple>
#include <array>
#include <stdexcept>
using namespace std;
enum ELogLevel {
Info,
Warn,
Debug,
Error,
Critical
};
static constexpr size_t constexprStringHash( char const* const str ) noexcept
{
return (
( *str != 0 ) ?
( static_cast< size_t >( *str ) + 33 * constexprStringHash( str + 1 ) ) :
5381
);
}
class EnumConverter final
{
public:
EnumConverter() = delete;
EnumConverter( const EnumConverter& ) = delete;
EnumConverter( EnumConverter&& ) = delete;
EnumConverter& operator =( const EnumConverter& ) = delete;
EnumConverter& operator =( EnumConverter&& ) = delete;
template< typename ENUM_T >
static constexpr const char* toStr( const ENUM_T value )
{
const auto& strEnumArray{ StrEnumContainer< ENUM_T >::StrEnumPairs };
const char* result{ nullptr };
for( size_t index{ 0 }; index < strEnumArray.size(); ++index ) {
if( std::get< 1 >( strEnumArray[ index ] ) == value ) {
result = std::get< 0 >( strEnumArray[ index ] );
break;
}
}
return ( ( result == nullptr ) ? throw std::logic_error{ "Enum toStrBase conversion failed" } : result );
}
template< typename ENUM_T >
static constexpr ENUM_T fromStr( const char* const str )
{
const auto& strEnumArray{ StrEnumContainer< ENUM_T >::StrEnumPairs };
const size_t hash{ constexprStringHash( str ) };
const ENUM_T* result{ nullptr };
for( size_t index{ 0 }; index < strEnumArray.size(); ++index ) {
if( std::get< 2 >( strEnumArray[ index ] ) == hash ) {
result = &( std::get< 1 >( strEnumArray[ index ] ) );
}
}
return ( ( result == nullptr ) ? throw std::logic_error{ "Enum toStrBase conversion failed" } : *result );
}
private:
template< typename ENUM_T, size_t LEN >
using ARRAY_T = std::array< std::tuple< const char* const, const ENUM_T, const size_t >, LEN >;
template< typename ENUM_T >
static constexpr std::tuple< const char* const, ENUM_T, size_t > getTuple( const char* const str, const ENUM_T type ) noexcept
{
return std::tuple< const char* const, ENUM_T, size_t >{ str, type, constexprStringHash( str ) };
}
template< typename ENUM_T >
struct StrEnumContainer
{
};
template< typename ENUM_T >
friend struct StrEnumContainer;
};
template<>
struct EnumConverter::StrEnumContainer< ELogLevel >
{
using ENUM_T = ELogLevel;
static constexpr EnumConverter::ARRAY_T< ENUM_T, 5 > StrEnumPairs{ {
{ getTuple( "Info", ENUM_T::Info ) },
{ getTuple( "Warn", ENUM_T::Warn ) },
{ getTuple( "Debug", ENUM_T::Debug ) },
{ getTuple( "Error", ENUM_T::Error ) },
{ getTuple( "Critical", ENUM_T::Critical ) },
} };
};
int main()
{
//static_assert( EnumConverter::fromStr< ELogLevel >( "Info" ) == EnumConverter::fromStr< ELogLevel >( EnumConverter::toStr( Error ) ), "Error" ); // Error
static_assert(
EnumConverter::toStr( Warn )[ 0 ] == 'W' &&
EnumConverter::toStr( Warn )[ 1 ] == 'a' &&
EnumConverter::toStr( Warn )[ 2 ] == 'r' &&
EnumConverter::toStr( Warn )[ 3 ] == 'n',
"Error"
);
static_assert( EnumConverter::fromStr< ELogLevel >( "Info" ) == EnumConverter::fromStr< ELogLevel >( EnumConverter::toStr( Info ) ), "Error" );
}
Related
Let's suppose I have a class with a private method is_escape which check if an input string is and ANSI escape sequence. This method is then used in another public method, into an if/else condition:
#include <string>
enum class ANSI { first, generic };
template <class T_str>
class foo
{
private:
template <typename T>
static constexpr bool is_escape( const T& str, ANSI&& flag ) { /* implementation */ }
public:
template <class T>
void public_method( T str ) // T can be also an int, double etc...
{
if ( is_escape( str ) ) { /* do something */ }
}
};
The implementation of the is_escape method is the following:
#include <string>
#include <type_traits>
template <typename T>
static constexpr bool is_escape( const T& str, ANSI&& flag )
{
if constexpr( std::is_convertible_v <T, std::basic_string_view<T_str>> && ! std::is_same_v<T, std::nullptr_t> )
{
switch( flag )
{
case( ANSI::first ):
{
return ( ! std::basic_string_view<T_str>( str ).rfind( "\033"s, 0 ) ) &&
( std::basic_string_view<T_str>( str ).length() < 7 );
}
case( ANSI::generic ):
{
return ( std::basic_string_view<T_str>( str ).find( "\033"s ) != std::basic_string_view<T_str>::npos );
}
}
}
return false;
}
Is there a better way to write the method in order to improve its performances (in C++17)? Thanks.
At the end, I improved the performances of the function with this signature:
template <typename T>
static constexpr bool is_escape( const T& str, const ANSI& flag )
{
if constexpr( std::is_convertible_v <T, std::basic_string_view<T_str>> && ! std::is_same_v<T, std::nullptr_t> )
{
switch( flag )
{
case( ANSI::first ):
{
return ( std::basic_string_view<T_str>( str ).length() < 7 ) && ( str[0] == '\033' );
}
case( ANSI::generic ):
{
return ( std::basic_string_view<T_str>( str ).find( '\033' ) != std::basic_string_view<T_str>::npos );
}
}
}
return false;
}
How do I do the following with std::cout?
double my_double = 42.0;
char str[12];
printf_s("%11.6lf", my_double); // Prints " 42.000000"
I am just about ready to give up and use sprintf_s.
More generally, where can I find a reference on std::ostream formatting that lists everything in one place, rather than spreading it all out in a long tutorial?
EDIT Dec 21, 2017 - See my answer below. It uses features that were not available when I asked this question in 2012.
std::cout << std::fixed << std::setw(11) << std::setprecision(6) << my_double;
You need to add
#include <iomanip>
You need stream manipulators
You may "fill" the empty places with whatever char you want. Like this:
std::cout << std::fixed << std::setw(11) << std::setprecision(6)
<< std::setfill('0') << my_double;
std::cout << boost::format("%11.6f") % my_double;
You have to #include <boost\format.hpp>
In C++20 you can to do
double my_double = 42.0;
char str[12];
std::format_to_n(str, sizeof(str), "{:11.6}", my_double);
or
std::string s = std::format("{:11.6}", my_double);
In pre-C++20 you can use the {fmt} library that provides an implementation of format_to_n.
Disclaimer: I'm the author of {fmt} and C++20 std::format.
In general, you want to avoid specifying things like 11 and 6 at the
point of output. That's physical markup, and you want logical markup;
e.g. pressure, or volume. That way, you define in a single place
how pressure or volume are formatted, and if that formatting changes,
you don't have to search through out the program to find where to change
the format (and accidentally change the format of something else). In
C++, you do this by defining a manipulator, which sets the various
formatting options, and preferrably restores them at the end of the full
expression. So you end up writing things like:
std::cout << pressure << my_double;
Although I definitly wouldn't use it in production code, I've found the
following FFmt formatter useful for quicky jobs:
class FFmt : public StateSavingManip
{
public:
explicit FFmt(
int width,
int prec = 6,
std::ios::fmtflags additionalFlags
= static_cast<std::ios::fmtflags>(),
char fill = ' ' );
protected:
virtual void setState( std::ios& targetStream ) const;
private:
int myWidth;
int myPrec;
std::ios::fmtflags myFlags;
char myFill;
};
FFmt::FFmt(
int width,
int prec,
std::ios::fmtflags additionalFlags,
char fill )
: myWidth( width )
, myPrec( prec )
, myFlags( additionalFlags )
, myFill( fill )
{
myFlags &= ~ std::ios::floatfield
myFlags |= std::ios::fixed
if ( isdigit( static_cast< unsigned char >( fill ) )
&& (myFlags & std::ios::adjustfield) == 0 ) {
myFlags |= std::ios::internal
}
}
void
FFmt::setState(
std::ios& targetStream ) const
{
targetStream.flags( myFlags )
targetStream.width( myWidth )
targetStream.precision( myPrec )
targetStream.fill( myFill )
}
This allows writing things like:
std::cout << FFmt( 11, 6 ) << my_double;
And for the record:
class StateSavingManip
{
public:
StateSavingManip(
StateSavingManip const& other );
virtual ~StateSavingManip();
void operator()( std::ios& stream ) const;
protected:
StateSavingManip();
private:
virtual void setState( std::ios& stream ) const = 0;
private:
StateSavingManip& operator=( StateSavingManip const& );
private:
mutable std::ios* myStream;
mutable std::ios::fmtflags
mySavedFlags;
mutable int mySavedPrec;
mutable char mySavedFill;
};
inline std::ostream&
operator<<(
std::ostream& out,
StateSavingManip const&
manip )
{
manip( out );
return out;
}
inline std::istream&
operator>>(
std::istream& in,
StateSavingManip const&
manip )
{
manip( in );
return in;
}
StateSavingManip.cc:
namespace {
// We maintain the value returned by ios::xalloc() + 1, and not
// the value itself. The actual value may be zero, and we need
// to be able to distinguish it from the 0 resulting from 0
// initialization. The function getXAlloc() returns this value
// -1, so we add one in the initialization.
int getXAlloc();
int ourXAlloc = getXAlloc() + 1;
int
getXAlloc()
{
if ( ourXAlloc == 0 ) {
ourXAlloc = std::ios::xalloc() + 1;
assert( ourXAlloc != 0 );
}
return ourXAlloc - 1;
}
}
StateSavingManip::StateSavingManip()
: myStream( NULL )
{
}
StateSavingManip::StateSavingManip(
StateSavingManip const&
other )
{
assert( other.myStream == NULL );
}
StateSavingManip::~StateSavingManip()
{
if ( myStream != NULL ) {
myStream->flags( mySavedFlags );
myStream->precision( mySavedPrec );
myStream->fill( mySavedFill );
myStream->pword( getXAlloc() ) = NULL;
}
}
void
StateSavingManip::operator()(
std::ios& stream ) const
{
void*& backptr = stream.pword( getXAlloc() );
if ( backptr == NULL ) {
backptr = const_cast< StateSavingManip* >( this );
myStream = &stream;
mySavedFlags = stream.flags();
mySavedPrec = stream.precision();
mySavedFill = stream.fill();
}
setState( stream );
}
#include <iostream>
#include <iomanip>
int main() {
double my_double = 42.0;
std::cout << std::fixed << std::setw(11)
<< std::setprecision(6) << my_double << std::endl;
return 0;
}
For future visitors who prefer actual printf-style format specs with std::ostream, here is yet another variation, based on Martin York's excellent post in another SO question: https://stackoverflow.com/a/535636:
#include <iostream>
#include <iomanip>
#include <stdio.h> //snprintf
class FMT
{
public:
explicit FMT(const char* fmt): m_fmt(fmt) {}
private:
class fmter //actual worker class
{
public:
explicit fmter(std::ostream& strm, const FMT& fmt): m_strm(strm), m_fmt(fmt.m_fmt) {}
//output next object (any type) to stream:
template<typename TYPE>
std::ostream& operator<<(const TYPE& value)
{
// return m_strm << "FMT(" << m_fmt << "," << value << ")";
char buf[40]; //enlarge as needed
snprintf(buf, sizeof(buf), m_fmt, value);
return m_strm << buf;
}
private:
std::ostream& m_strm;
const char* m_fmt;
};
const char* m_fmt; //save fmt string for inner class
//kludge: return derived stream to allow operator overloading:
friend FMT::fmter operator<<(std::ostream& strm, const FMT& fmt)
{
return FMT::fmter(strm, fmt);
}
};
usage example:
double my_double = 42.0;
cout << FMT("%11.6f") << my_double << "more stuff\n";
or even:
int val = 42;
cout << val << " in hex is " << FMT(" 0x%x") << val << "\n";
it's me, the OP, Jive Dadson - five years on. C++17 is becoming a reality.
The advent of variadic template parameters with perfect forwarding has made life so much simpler. The chained madness of ostream<< and boost::format% can be dispensed with. The function oprintf below fills the bill. Work in progress. Feel free to chime in on error-handling, etc...
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <string_view>
namespace dj {
template<class Out, class... Args>
Out& oprintf(Out &out, const std::string_view &fmt, Args&&... args) {
const int sz = 512;
char buffer[sz];
int cx = snprintf(buffer, sz, fmt.data(), std::forward<Args>(args)...);
if (cx >= 0 && cx < sz) {
return out.write(buffer, cx);
} else if (cx > 0) {
// Big output
std::string buff2;
buff2.resize(cx + 1);
snprintf(buff2.data(), cx, fmt.data(), std::forward<Args>(args)...);
return out.write(buff2.data(), cx);
} else {
// Throw?
return out;
}
}
}
int main() {
const double my_double = 42.0;
dj::oprintf(std::cout, "%s %11.6lf\n", "My double ", my_double);
return 0;
}
Some great answers already; kudos to those!
This is based on some of them. I have added type assertions for POD types, since they are the only safe types usable with printf().
#include <iostream>
#include <stdio.h>
#include <type_traits>
namespace fmt {
namespace detail {
template<typename T>
struct printf_impl
{
const char* fmt;
const T v;
printf_impl(const char* fmt, const T& v) : fmt(fmt), v(v) {}
};
template<typename T>
inline typename std::enable_if<std::is_pod<T>::value, std::ostream& >::type
operator<<(std::ostream& os, const printf_impl<T>& p)
{
char buf[40];
::snprintf(buf, sizeof(buf), p.fmt, p.v, 40);
return os << buf;
}
} // namespace detail
template<typename T>
inline typename std::enable_if<std::is_pod<T>::value, detail::printf_impl<T> >::type
printf(const char* fmt, const T& v)
{
return detail::printf_impl<T>(fmt, v);
}
} // namespace fmt
Example usage it as below.
std::cout << fmt::printf("%11.6f", my_double);
Give it a try on Coliru.
I have mutually recursive variant types: Value, Array, and Object. The crux of the matter is that I can assign one of the variant types to a Value when it is nested within an Array or Object, which can both contain Value. I cannot assign directly to a Value. The main() function code at the bottom should make this clear. The compiler (GNU GCC 7.2) gives me errors like:
error: conversion from 'JSON::Integer {aka long unsigned int}' to non-scalar type 'JSON::Value' requested
Value v = Integer( 7 );
and oddly does not provide any additional information about assignment operator candidates or anything else. Further, it isn't clear to me how assignment operators I have defined in Value are affecting this problem. Complete working example code follows. Any assignment of one of the variant types directly to a Value type in main() causes a compiler error just like the one above for the Integer type.
Thanks in advance!
Object Class
#ifndef JSON_OBJECT_HPP
#define JSON_OBJECT_HPP
#include <iomanip>
#include <string>
#include <unordered_map>
#include "Value.hpp"
namespace JSON {
class Object {
public:
using Key = std::string;
private:
using values_t = std::unordered_map<Key,Value>;
values_t values;
public:
Object() = default;
Value & operator[]( Key const & key ) {
auto it = values.emplace( key, Value() );
return it.first->second;
}
Value const & operator[]( Key const & key ) const {
auto it = values.find( key );
return it->second;
}
bool has_key( Key const & key ) const {
auto it = values.find( key );
return it != values.end();
}
bool operator==( Object const & rhs ) const {
return values == rhs.values;
}
values_t::const_iterator begin() const {
return values.begin();
}
values_t::const_iterator end( ) const {
return values.end();
}
values_t::iterator begin() {
return values.begin();
}
values_t::iterator end() {
return values.end();
}
};
bool operator==( Object const & lhs, Object const & rhs ) {
return lhs.operator==( rhs );
}
std::ostream & operator<<( std::ostream & os, Object const & object ) {
os << '{';
auto begin = object.begin();
auto end = object.end();
if( begin != end ) {
os << std::quoted( begin->first ) << ':' << begin->second;
}
while( ++begin != end ) {
os << ',' << std::quoted( begin->first ) << ':' << begin->second;
}
os << '}';
}
}
#endif
Array Class
#ifndef JSON_ARRAY_HPP
#define JSON_ARRAY_HPP
#include <vector>
#include "types.hpp"
#include "Value.hpp"
namespace JSON {
std::ostream & operator<<( std::ostream &, Value & );
class Array {
private:
using values_t = std::vector<Value>;
values_t values;
public:
using Key = values_t::size_type;
Array() = default;
Value & operator[]( Key key ) {
if( !has_key( key ) ) {
values.resize( key + 1 );
}
return values[key];
}
Value const & operator[]( Key key ) const {
return values[key];
}
bool has_key( Key key ) const {
return key < values.size();
}
bool operator==( Array const & rhs ) const {
return values == rhs.values;
}
values_t::const_iterator begin() const {
return values.begin();
}
values_t::const_iterator end( ) const {
return values.end();
}
values_t::iterator begin() {
return values.begin();
}
values_t::iterator end() {
return values.end();
}
};
bool operator==( Array const & lhs, Array const & rhs ) {
return lhs.operator==( rhs );
}
std::ostream & operator<<( std::ostream & os, Array const & array ) {
os << '[';
auto begin = array.begin();
auto end = array.end();
if( begin != end ) {
os << *begin;
}
while( ++begin != end ) {
os << ',' << *begin;
}
os << ']';
}
}
#endif
Value Class
#ifndef JSON_VALUE_HPP
#define JSON_VALUE_HPP
#include <iomanip>
#include <type_traits>
#include <variant>
#include <boost/variant/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include "types.hpp"
namespace JSON {
class Object;
class Array;
bool operator==( Object const & lhs, Object const & rhs );
bool operator==( Array const & lhs, Array const & rhs );
std::ostream & operator<<( std::ostream &, Object const & object );
std::ostream & operator<<( std::ostream &, Array const & array );
template<class T> struct always_false : std::false_type {};
class Value {
private:
using variant_t = std::variant<Undefined,String,Integer,Number,boost::recursive_wrapper<Object>,boost::recursive_wrapper<Array> >;
variant_t data;
friend std::ostream & operator<<( std::ostream & os, Value const & value );
public:
Value() = default;
bool operator==( Value const & rhs ) const {
return std::visit(
[]( auto && lhs, auto && rhs ) -> bool {
using lhsT = std::decay_t<decltype( lhs )>;
using rhsT = std::decay_t<decltype( rhs )>;
if constexpr ( std::is_same_v< lhsT, rhsT> ) {
if constexpr (std::is_same_v< lhsT, boost::recursive_wrapper<Object> > ) {
return lhs.get() == rhs.get();
} else if constexpr (std::is_same_v< lhsT, boost::recursive_wrapper<Array> > ) {
return lhs.get() == rhs.get();
} else {
return lhs == rhs;
}
} else {
return false;
}
},
data,
rhs.data
);
}
Value & operator=( String const & rhs ) {
data = rhs;
return *this;
}
Value & operator=( Integer const & rhs ) {
data = rhs;
return *this;
}
Value & operator=( Object const & rhs ) {
data = rhs;
return *this;
}
Value & operator=( Array const & rhs ) {
data = rhs;
return *this;
}
};
std::ostream & operator<<( std::ostream & os, Value const & value ) {
std::visit(
[&os]( auto && arg ) {
using T = std::decay_t<decltype( arg )>;
if constexpr ( std::is_same_v< T, Undefined > ) {
os << "undefined";
} else if constexpr ( std::is_same_v< T, String > ) {
os << std::quoted( arg );
} else if constexpr ( std::is_same_v< T, Integer > ) {
os << arg;
} else if constexpr ( std::is_same_v< T, Number > ) {
os << arg;
} else if constexpr ( std::is_same_v< T, boost::recursive_wrapper<Object> > ) {
os << arg.get();
} else if constexpr ( std::is_same_v< T, boost::recursive_wrapper<Array> > ) {
os << arg.get();
} else if constexpr ( std::is_same_v< T, Boolean > ) {
os << (arg == false ? "false" : "true");
} else if constexpr ( std::is_same_v< T, Null > ) {
os << "null";
} else {
static_assert( always_false<T>::value, "non-exhaustive visitor" );
}
},
value.data
);
}
}
#endif
Type Definitions
#ifndef TYPES_HPP
#define TYPES_HPP
namespace JSON {
template <typename Tag> struct Literal {
bool operator==( Literal const & ) const {
return true;
}
bool operator<( Literal const & ) const {
return false;
}
};
using String = std::string;
using Integer = uint64_t;
using Number = double;
class Object;
class Array;
using Boolean = bool;
using Null = Literal<struct tag_null>;
using Undefined = Literal<struct tag_undefined>;
}
#endif
Main Code
#include <iostream>
#include "Array.hpp"
#include "Object.hpp"
#include "Value.hpp"
using namespace JSON;
int main() {
Object o;
o["fun"] = "what?"; // compiles fin
o["stuff"] = "yeah!"; // compiles fine
o["inttest"] = Integer( 44 ); // compiles fine
Array a;
a[2] = "yo"; // compiles fine
a[3] = Integer( 6 ); // compiles fine
o["arrtest"] = a;
// Value v = a; // fails to compile
Value v = Integer( 7 ); // fails to compile
std::cout << v;
std::cout << o << "\n";
std::cout << a << "\n";
return 0;
}
This was a bad question with an embarrassing oversight. As the commenter pointed out, the problematic lines in main() were initializations rather than assignments. Providing the appropriate constructors solved the problem. For completeness, corrected code follows. It compiles and works fine under GNU GCC 7.2.
Object Class
#ifndef JSON_OBJECT_HPP
#define JSON_OBJECT_HPP
#include <iomanip>
#include <string>
#include <unordered_map>
#include "Value.hpp"
namespace JSON {
class Object {
public:
using Key = std::string;
private:
using values_t = std::unordered_map<Key,Value>;
values_t values;
public:
Object() = default;
Value & operator[]( Key const & key ) {
auto it = values.emplace( key, Value() );
return it.first->second;
}
Value const & operator[]( Key const & key ) const {
auto it = values.find( key );
return it->second;
}
bool has_key( Key const & key ) const {
auto it = values.find( key );
return it != values.end();
}
bool operator==( Object const & rhs ) const {
return values == rhs.values;
}
values_t::const_iterator begin() const {
return values.begin();
}
values_t::const_iterator end( ) const {
return values.end();
}
values_t::iterator begin() {
return values.begin();
}
values_t::iterator end() {
return values.end();
}
};
bool operator==( Object const & lhs, Object const & rhs ) {
return lhs.operator==( rhs );
}
std::ostream & operator<<( std::ostream & os, Object const & object ) {
os << '{';
auto begin = object.begin();
auto end = object.end();
if( begin != end ) {
os << std::quoted( begin->first ) << ':' << begin->second;
}
while( ++begin != end ) {
os << ',' << std::quoted( begin->first ) << ':' << begin->second;
}
os << '}';
}
}
#endif
Array Class
#ifndef JSON_ARRAY_HPP
#define JSON_ARRAY_HPP
#include <vector>
#include "types.hpp"
#include "Value.hpp"
namespace JSON {
std::ostream & operator<<( std::ostream &, Value const & );
class Array {
private:
using values_t = std::vector<Value>;
values_t values;
public:
using Key = values_t::size_type;
Array() = default;
Value & operator[]( Key key ) {
if( !has_key( key ) ) {
values.resize( key + 1 );
}
return values[key];
}
Value const & operator[]( Key key ) const {
return values[key];
}
bool has_key( Key key ) const {
return key < values.size();
}
bool operator==( Array const & rhs ) const {
return values == rhs.values;
}
values_t::const_iterator begin() const {
return values.begin();
}
values_t::const_iterator end( ) const {
return values.end();
}
values_t::iterator begin() {
return values.begin();
}
values_t::iterator end() {
return values.end();
}
};
bool operator==( Array const & lhs, Array const & rhs ) {
return lhs.operator==( rhs );
}
std::ostream & operator<<( std::ostream & os, Array const & array ) {
os << '[';
auto begin = array.begin();
auto end = array.end();
if( begin != end ) {
os << *begin;
}
while( ++begin != end ) {
os << ',' << *begin;
}
os << ']';
}
}
#endif
Value Class
#ifndef JSON_VALUE_HPP
#define JSON_VALUE_HPP
#include <iomanip>
#include <type_traits>
#include <variant>
#include <boost/variant/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include "types.hpp"
namespace JSON {
class Object;
class Array;
bool operator==( Object const & lhs, Object const & rhs );
bool operator==( Array const & lhs, Array const & rhs );
std::ostream & operator<<( std::ostream &, Object const & object );
std::ostream & operator<<( std::ostream &, Array const & array );
template<class T> struct always_false : std::false_type {};
class Value {
private:
using variant_t = std::variant<Undefined,String,Integer,Number,boost::recursive_wrapper<Object>,boost::recursive_wrapper<Array> >;
variant_t data;
friend std::ostream & operator<<( std::ostream & os, Value const & value );
public:
Value() = default;
template <typename T> Value( T const & rhs ) : data( rhs ) {}
bool operator==( Value const & rhs ) const {
return std::visit(
[]( auto && lhs, auto && rhs ) -> bool {
using lhsT = std::decay_t<decltype( lhs )>;
using rhsT = std::decay_t<decltype( rhs )>;
if constexpr ( std::is_same_v< lhsT, rhsT> ) {
if constexpr (std::is_same_v< lhsT, boost::recursive_wrapper<Object> > ) {
return lhs.get() == rhs.get();
} else if constexpr (std::is_same_v< lhsT, boost::recursive_wrapper<Array> > ) {
return lhs.get() == rhs.get();
} else {
return lhs == rhs;
}
} else {
return false;
}
},
data,
rhs.data
);
}
};
std::ostream & operator<<( std::ostream & os, Value const & value ) {
std::visit(
[&os]( auto && arg ) {
using T = std::decay_t<decltype( arg )>;
if constexpr ( std::is_same_v< T, Undefined > ) {
os << "undefined";
} else if constexpr ( std::is_same_v< T, String > ) {
os << std::quoted( arg );
} else if constexpr ( std::is_same_v< T, Integer > ) {
os << arg;
} else if constexpr ( std::is_same_v< T, Number > ) {
os << arg;
} else if constexpr ( std::is_same_v< T, boost::recursive_wrapper<Object> > ) {
os << arg.get();
} else if constexpr ( std::is_same_v< T, boost::recursive_wrapper<Array> > ) {
os << arg.get();
} else if constexpr ( std::is_same_v< T, Boolean > ) {
os << (arg == false ? "false" : "true");
} else if constexpr ( std::is_same_v< T, Null > ) {
os << "null";
} else {
static_assert( always_false<T>::value, "non-exhaustive visitor" );
}
},
value.data
);
}
}
#endif
Type Definitions
#ifndef JSON_TYPES_HPP
#define JSON_TYPES_HPP
namespace JSON {
template <typename Tag> struct Literal {
bool operator==( Literal const & ) const {
return true;
}
bool operator<( Literal const & ) const {
return false;
}
};
using String = std::string;
using Integer = uint64_t;
using Number = double;
class Object;
class Array;
using Boolean = bool;
using Null = Literal<struct tag_null>;
using Undefined = Literal<struct tag_undefined>;
}
#endif
Main Code
#include <iostream>
#include "Array.hpp"
#include "Object.hpp"
#include "Value.hpp"
using namespace JSON;
int main() {
Object o;
o["fun"] = "what?";
o["stuff"] = "yeah!";
o["inttest"] = Integer( 44 );
Array a;
a[2] = "yo";
a[3] = Integer( 6 );
o["arrtest"] = a;
// Value v = a;
Value v = Integer( 7 );
std::cout << v << "\n";
std::cout << o << "\n";
std::cout << a << "\n";
return 0;
}
I have a vector full with structs look like this
struct person_t
{
string name;
string id;
struct location_t location;
};
vector <person_t> myvector;
and i have read in the items in myvector
but now i need to know how to search for specific item in vector and count how many of that item it is in the vector.
unsigned int count = std::count_if( myvector.begin(), myvector.begin(),
[]( const person_t & p ) { return p.name == "Bill Gates"; }
);
or without c++11
struct EqualByName {
EqualByName( const std::string & name ) : m_name( name ) {}
bool operator()( const person_t & p ) const { return p.name == m_name; }
private:
std::string m_name;
};
unsigned int count = std::count_if( myvector.begin(), myvector.begin(),
EqualByName( "Bill Gates" )
);
or ugly looking but for all occasions ))
template< class T, class FieldType, FieldType T::*FieldPtr >
struct EqualBy
{
EqualBy( const FieldType & value ) : m_fieldValue( value ) {}
bool operator()( const T & r ) const {
return m_fieldValue == r.*FieldPtr;
}
bool operator()( const T * p ) const {
return m_fieldValue == p->*FieldPtr;
}
private:
const FieldType m_fieldValue;
};
// usage:
typedef EqualBy< person_t, std::string, & person_t::name > EqualByName;
typedef EqualBy< person_t, std::string, & person_t::id > EqualById;
unsigned int namesCount = std::count_if( myvector.begin(), myvector.end(),
EqualByName( "Bill Gates" )
);
unsigned int idsCount = std::count_if( myvector.begin(), myvector.end(),
EqualById( "123456" )
);
The code below implements a dichotomic search to find if a runtime integral value exist in an enum. Because enum are compile time known, the code try to generate the sorted array of the enum values in a constexpr way and have a constexpr compatible find.
#include <iostream>
#include <type_traits>
// ============================
using std::cout; using std::endl;
constexpr char const * const el = "\n";
// ============================
template < typename T_, typename = typename std::enable_if<std::is_enum<T_>::value>::type >
std::ostream & operator<<( std::ostream & os, T_ const & v ) {
return os << static_cast<typename std::underlying_type<T_>::type>(v)<< " ";
}
// ============================
// Minimum support for a constexpr array because std::array::operator[] lacks constexpr
template <typename T_, size_t N_>
struct Array {
constexpr size_t size() const { return N_; }
constexpr T_& operator[]( size_t i ) { return values_[i]; }
constexpr T_ const& operator[]( size_t i ) const { return values_[i]; }
constexpr T_* begin() { return values_; }
constexpr T_* end() { return values_+N_; }
constexpr T_ const * begin() const { return values_; }
constexpr T_ const * end() const { return values_+N_; }
T_ values_[N_];
};
// ============================
// return a sorted copy of the array argument
template <typename T_, size_t N_>
constexpr Array<T_, N_> const_sort( Array<T_, N_> tab ) {
Array<T_, N_> result{ T_{} };
for( size_t i{}; i != N_; ++i )
result[i] = tab[i];
for( size_t i{}; i != N_-1; ++i ) {
size_t min {i};
for( size_t j{ i+1 }; j != N_; ++j ) {
if( result[j] < result[min] )
min = j;
}
if( min != i ) {
auto tmp = std::move( result[i] );
result[i] = std::move( result[min] );
result[min] = std::move( tmp );
}
}
return result;
}
// ============================
// The user has to specialize UnsortedFunc to return an Array<EnumType,N>
template <typename T_>
constexpr auto const UnsortedFunc();
template <typename T_>
constexpr const decltype(UnsortedFunc<T_>()) Unsorted = UnsortedFunc<T_>();
// ============================
template < typename T_ >
constexpr decltype( const_sort( Unsorted<T_> ) ) Sorted = const_sort( Unsorted<T_> );
// ============================
// check for existence of a matching enum member equal to an integral value.
// The user has to feed a specialization of UnsortedFunc with the enum values.
template< typename E_>
constexpr bool Contains( typename std::underlying_type<E_>::type v ) {
using T = typename std::underlying_type<E_>::type;
T min = static_cast<T>(Sorted<E_>[0]);
T max = static_cast<T>(Sorted<E_>[Sorted<E_>.size()-1]);
if ( v < min || max < v )
return false;
size_t low {}, high { Sorted<E_>.size() };
while ( low < high ) {
size_t mid = (high + low ) / 2;
if ( v < static_cast<T>(Sorted<E_>[ mid ]) ) {
high = mid;
} else {
if ( !( static_cast<T>(Sorted<E_>[ mid ]) < v ) )
return true;
low = mid+1;
}
}
return false;
}
template < typename T_ >
void Dump() {
for( auto & v : Unsorted<T_> )
cout << v << " ";
cout << el;
for( auto & v : Sorted<T_> )
std::cout << v << " ";
cout << el;
}
enum class Foo : int { a=5, b=2, c = 8, d = 1, e = 4, f = 9 };
template <> constexpr auto const UnsortedFunc<Foo>() {
return Array<Foo,6>{ Foo::a, Foo::b, Foo::c, Foo::d, Foo::e, Foo::f };
}
By playing with this code in the main function, i can have a lot of different of warnings and errors.
That one compile and link but i had to dump the two arrays first, still with a warning :
int main() {
cout << std::boolalpha << __VERSION__ << el;
for( auto & v : Unsorted<Foo> )
cout << v << " ";
cout << el;
for( auto & v : Sorted<Foo> )
std::cout << v << " ";
cout << el;
constexpr bool b1 = Contains<Foo>(2);
constexpr bool b2 = Contains<Foo>(10);
cout << b1 << " " << b2 << el;
for( int i{}; i != 10; ++i )
cout << i << " : " << Contains<Foo>(i) << ", ";
}
And the output :
main.cpp:60:46: warning: variable 'Unsorted<type-parameter-0-0>' has internal linkage but is not defined [-Wundefined-internal]
constexpr const decltype(UnsortedFunc<T_>()) Unsorted = UnsortedFunc<T_>();
^
main.cpp:64:71: note: used here
constexpr decltype( const_sort( Unsorted<T_> ) ) Sorted = const_sort( Unsorted<T_> );
^
1 warning generated.
4.2.1 Compatible Clang 3.5 (trunk 198621)
5 2 8 1 4 9
1 2 4 5 8 9
true false
0 : false, 1 : true, 2 : true, 3 : false, 4 : true, 5 : true, 6 : false, 7 : false, 8 : true, 9 : true,
If i remove the two array dump, i have compilation errors, b1 and b2 cannot anymore be evaluated as constexpr expressions :
int main() {
cout << std::boolalpha << __VERSION__ << el;
constexpr bool b1 = Contains<Foo>(2);
constexpr bool b2 = Contains<Foo>(10);
cout << b1 << " " << b2 << el;
for( int i{}; i != 10; ++i )
cout << i << " : " << Contains<Foo>(i) << ", ";
}
With the output :
main.cpp:116:20: error: constexpr variable 'b1' must be initialized by a constant expression
constexpr bool b1 = Contains<Foo>(2);
^ ~~~~~~~~~~~~~~~~
main.cpp:72:28: note: subexpression not valid in a constant expression
T min = static_cast<T>(Sorted<E_>[0]);
^
main.cpp:116:25: note: in call to 'Contains(2)'
constexpr bool b1 = Contains<Foo>(2);
^
main.cpp:117:20: error: constexpr variable 'b2' must be initialized by a constant expression
constexpr bool b2 = Contains<Foo>(10);
^ ~~~~~~~~~~~~~~~~~
main.cpp:72:28: note: subexpression not valid in a constant expression
T min = static_cast<T>(Sorted<E_>[0]);
^
main.cpp:117:25: note: in call to 'Contains(10)'
constexpr bool b2 = Contains<Foo>(10);
^
2 errors generated.
Last, if i try to dump the arrays with the Dump function, link errors arrive too :
int main() {
cout << std::boolalpha << __VERSION__ << el;
Dump<Foo>();
}
With the output :
main.cpp:60:46: warning: variable 'Unsorted<type-parameter-0-0>' has internal linkage but is not defined [-Wundefined-internal]
constexpr const decltype(UnsortedFunc<T_>()) Unsorted = UnsortedFunc<T_>();
^
main.cpp:64:71: note: used here
constexpr decltype( const_sort( Unsorted<T_> ) ) Sorted = const_sort( Unsorted<T_> );
^
main.cpp:60:46: warning: variable 'Unsorted<Foo>' has internal linkage but is not defined [-Wundefined-internal]
constexpr const decltype(UnsortedFunc<T_>()) Unsorted = UnsortedFunc<T_>();
^
main.cpp:93:21: note: used here
for( auto & v : Unsorted<T_> )
^
main.cpp:64:50: warning: variable 'Sorted<Foo>' has internal linkage but is not defined [-Wundefined-internal]
constexpr decltype( const_sort( Unsorted<T_> ) ) Sorted = const_sort( Unsorted<T_> );
^
main.cpp:97:21: note: used here
for( auto & v : Sorted<T_> )
^
3 warnings generated.
/tmp/main-22b320.o: In function `void Dump<Foo>()':
main.cpp:(.text._Z4DumpI3FooEvv[_Z4DumpI3FooEvv]+0xc): undefined reference to `_ZL8UnsortedI3FooE'
main.cpp:(.text._Z4DumpI3FooEvv[_Z4DumpI3FooEvv]+0x20): undefined reference to `_ZL8UnsortedI3FooE'
main.cpp:(.text._Z4DumpI3FooEvv[_Z4DumpI3FooEvv]+0x98): undefined reference to `_ZL6SortedI3FooE'
main.cpp:(.text._Z4DumpI3FooEvv[_Z4DumpI3FooEvv]+0xb0): undefined reference to `_ZL6SortedI3FooE'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Bonus question, on the Sorted and Unsorted definition line, if i use const auto instead of decltype :
main.cpp:73:49: error: member reference base type 'const auto' is not a structure or union
T max = static_cast<T>(Sorted<E_>[Sorted<E_>.size()-1]);
~~~~~~~~~~^~~~~
main.cpp:77:37: error: member reference base type 'const auto' is not a structure or union
size_t low {}, high { Sorted<E_>.size() };
~~~~~~~~~~^~~~~
main.cpp:77:20: error: variables defined in a constexpr function must be initialized
size_t low {}, high { Sorted<E_>.size() };
^
main.cpp:93:19: error: invalid range expression of type 'const auto'; no viable 'begin' function available
for( auto & v : Unsorted<T_> )
^ ~~~~~~~~
main.cpp:114:5: note: in instantiation of function template specialization 'Dump<Foo>' requested here
Dump<Foo>();
^
main.cpp:97:19: error: invalid range expression of type 'const auto'; no viable 'begin' function available
for( auto & v : Sorted<T_> )
^ ~~~~~~
5 errors generated.
As told by Jarod42, it was related to the bug he found.
With an up to date clang trunk depository ( version 4.2.1 Compatible Clang 3.5 (202594) ), everything is back to normal. I only had to write my own constexpr std::move as the Visual studio headers are not yet C++14 compliant.
Built ok with that command line clang-cl.exe /GR- -D_HAS_EXCEPTIONS=0 /O0 /FA /Wall -Xclang -std=c++1y main.cpp :
The final code :
#include <iostream>
#include <type_traits>
using std::cout;
using std::endl;
constexpr char const * const el = "\n";
// ============================
// VS2013 do not have constexpr supports in the standard library
namespace std14 {
template< class T >
constexpr typename std::remove_reference<T>::type&& move( T&& t ) {
return static_cast<typename std::remove_reference<T>::type&&>(t);
}
}
// ============================
// Minimum support for a constexpr array because std::array::operator[] lacks constexpr
template <typename T_, size_t N_>
struct Array {
constexpr size_t size() const { return N_; }
constexpr T_& operator[]( size_t i ) { return values_[i]; }
constexpr T_ const& operator[]( size_t i ) const { return values_[i]; }
constexpr T_* begin() { return values_; }
constexpr T_* end() { return values_+N_; }
constexpr T_ const * begin() const { return values_; }
constexpr T_ const * end() const { return values_+N_; }
T_ values_[N_];
};
// ============================
// return a sorted copy of the array argument
template <typename T_, size_t N_>
constexpr Array<T_, N_> const_sort( Array<T_, N_> tab ) {
Array<T_, N_> result{ { T_{} } };
for( size_t i{}; i != N_; ++i )
result[i] = tab[i];
for( size_t i{}; i != N_-1; ++i ) {
size_t min {i};
for( size_t j{ i+1 }; j != N_; ++j ) {
if( result[j] < result[min] )
min = j;
}
if( min != i ) {
using std14::move;
auto tmp = move(result[i]);
result[i] = move(result[min]);
result[min] = move(tmp);
}
}
return result;
}
// ============================
// The user has to specialize UnsortedFunc to return an Array<EnumType,N>
template <typename T_>
constexpr auto UnsortedFunc();
template <typename T_>
constexpr const decltype(UnsortedFunc<T_>()) Unsorted = UnsortedFunc<T_>();
// ============================
template < typename T_ >
constexpr decltype( const_sort( Unsorted<T_> ) ) Sorted = const_sort( Unsorted<T_> );
// ============================
// check for existence of a matching enum member equal to an integral value.
// The user has to feed a specialization of UnsortedFunc with the enum values.
template< typename E_>
constexpr bool Contains( std::underlying_type_t<E_> v ) {
using T = std::underlying_type_t<E_>;
constexpr auto & sorted = Sorted<E_>;
T min = static_cast<T>(sorted[0]);
T max = static_cast<T>(sorted[sorted.size()-1]);
if ( v < min || max < v )
return false;
size_t low {}, high { sorted.size() };
while ( low < high ) {
size_t mid = (high + low ) / 2;
if ( v < static_cast<T>(sorted[ mid ]) ) {
high = mid;
} else {
if ( !( static_cast<T>(sorted[ mid ]) < v ) )
return true;
low = mid+1;
}
}
return false;
}
enum class Foo : int { a = 5, b = 2, c = 8, d = 1, e = 4, f = 9 };
template <> constexpr auto UnsortedFunc<Foo>() {
return Array<Foo,6>{ { Foo::a, Foo::b, Foo::c, Foo::d, Foo::e, Foo::f } };
}
int main() {
cout << std::boolalpha << __VERSION__ << el;
constexpr bool b1 = Contains<Foo>(2);
cout << b1 << el;
for( int i{}; i != 10; ++i ) {
cout << i << " : " << Contains<Foo>(i) << ", ";
}
}