I'm learning about template in C++, and my set of C++ terminology is somewhat limited, so I couldn't google this problem.I'm trying to implement a custom dict type based on std::unordered_map. My goal is to be able to instantiate the class dict in ways like the following:
dict<std::string, long> d; // OR
dict<std::string, std::set<std::string>> d; // OR
dict<std::string, std::map<char, float>> d; // OR
dict<std::string, std::vector<std::string>> d; // OR
dict<std::string, std::vector<double>> d;
So here's the code, I'm using:
utils.h
#include <fstream>
#include <unordered_map>
#include <set>
#include <vector>
#include <algorithm>
#include <type_traits>
// for bravity
using namespace std;
// to check for stl vector
// slightly modified version of: https://stackoverflow.com/a/31105859
namespace is_container {
template <typename T> struct stl_vector : false_type{};
template <typename T> struct stl_vector<std::vector<T>> : true_type{};
}
namespace StringOps {
// generaic function to split based on many delimiters:
// source: https://stackoverflow.com/a/9676623
vector<string> split(const string& str, const string& delimiters = " ,") {
vector<string> v;
unsigned start = 0;
auto pos = str.find_first_of(delimiters, start);
while(pos != string::npos) {
if(pos != start) // ignore empty tokens
v.emplace_back(str, start, pos - start);
start = pos + 1;
pos = str.find_first_of(delimiters, start);
}
if(start < str.length()) // ignore trailing delimiter
v.emplace_back(str, start, str.length() - start); // add what's left of the string
return v;
}
}
template<class Key, template <class...> class Value, typename T, class = void>
class dict {
public:
Value<T> t;
};
template<class Key, template <class...> class Value, typename T> // detect container types with ::iterator
class dict<Key, Value, T, void_t<typename Value<T>::iterator>> : true_type {
private:
unordered_map<Key, Value<T>> content;
bool is_vector = false;
string line;
unordered_map<Key, Value<T>> load(ifstream& file) {
while (getline(file, line)) {
if (!line.empty()) {
// remove trailling \n if exists
if (line[line.length()-1] == '\n')
line.erase(line.length() - 1);
vector<string> tokens = StringOps::split(line);
Value<T> result;
(tokens[i]));
if (is_vector) {
for (unsigned i = 1; i < tokens.size(); i++) {
result.emplace_back(static_cast<T>(tokens[i]));
}
}
if(false) { // should never be looked into
auto it = result.cend();
for (unsigned i = 1; i < tokens.size(); i++) {
result.emplace_hint(it, static_cast<T>(tokens[i]));
}
}
content[static_cast<Key>(tokens[0])] = result;
}
}
return content;
}
public:
constexpr Value<T>& operator[](Key k) {
return content[k];
}
dict(const string& path) {
// detect vector type
if(is_container::stl_vector<decay_t<Value<T>>>::value)
is_vector = true;
ifstream file(path);
content = load(file);
}
constexpr unsigned size() {
return content.size();
}
};
template<class Key, template <class...T> class Value, typename T> // detect arithmatic types
class dict<Key, Value, T, typename enable_if<is_arithmetic<Value<T>>::value>::type> {
public:
dict() {
// we'll come to you later..
}
};
main.cpp
#include <iostream>
#include "utils.h"
int main() {
dict<string, vector, string> d("/home/path/to/some/file");
cout << d.size();
}
results:
error: no member named 'emplace_hint' in 'std::vector<std::__cxx11::basic_string<char>, std::allocator<std::__cxx11::basic_string<char> > >'
result.emplace_hint(it, static_cast<T>(tokens[i]));
questions:
1 - why on earth if (false) condition is reached in the first place?
2 - how could this be tweaked to achieve the desired instantiation style?
if (false) doesn't mean the code isn't compiled; it simply means the code inside is not executed at runtime, but it still has to be valid.
There are (at least) three kinds of conditional constructs in C++:
Preprocessor conditions. This tells the preprocessor not to pass the code to the compiler if if the condition is not met. Therefore, the code can be completely gibberish as long as it consists of valid preprocessor tokens. For example, the following is a well-formed C++ program with defined behavior:
#include <iostream>
int main()
{
#if 0
YYMJBNvOldLdK8rC0PTXH8DHJ58FQpP0MisPZECDuYHDJ7xL9G
#else
std::cout << "Hello world!\n";
#endif
}
Runtime selection statements. Such code is still parsed by the compiler and must still be valid code regardless of whether the compiler is capable of proving unreachable code — the compiler cannot even find the terminating } if it doesn't parse the code. This is partly because compilers cannot evaluate arbitrary expressions at runtime — if you don't specify explicitly (see next bullet), then evaluation defaults to be runtime. Therefore, the code above becomes ill-formed if you replace #if 0 – #else – #endif with if (false) { – } else { – }. However, runtime errors (i.e., undefined behavior) within unreachable code are fine. Therefore, the following is a well-formed C++ program with defined behavior: (some compilers may generate warnings, but that's irrelevant)
#include <iostream>
#include <climits>
int main()
{
if (false) {
volatile int x = 42/0;
x = *static_cast<int*>(nullptr);
const int y = INT_MAX + 1;
} else {
std::cout << "Hello world!\n";
}
}
(Since C++17) if constexpr. The rule for this one is a bit complex. The condition has to be known at compile time, and the false branch is discarded. The discarded branch is still required to be valid, except that it is not instantiated. Therefore, the code above is still valid code if you change if to if constexpr. The following is also a well-formed C++ program with defined behavior:
#include <iostream>
#include <type_traits>
template <typename T>
void print(T x)
{
if constexpr (std::is_same_v<T, int>) {
std::cout << static_cast<typename T::template stack<T>::overflow>(x);
} else {
std::cout << x;
}
}
int main()
{
print("Hello world!\n");
}
The typename and template are still necessary to make the code syntactically valid, but the nonexistent type const char*::stack<const char*>::overflow is not formed.
In your case, you can write a trait class to determine whether a type is a specialization of the class template std::vector: (here I use the standard traits convention)
template <typename C>
struct is_std_vector :std::false_type {};
template <typename T, typename A>
struct is_std_vector<std::vector<T, A>> :std::true_type {};
template <typename C>
inline constexpr bool is_std_vector_v = is_std_vector<C>::value;
Then use it in if constexpr to dispatch: (don't forget to replace Container with the container type you are examining)
if constexpr (is_std_vector_v<Container>) {
// do std::vector specific things
} else {
// do other things
}
Related
I ran into some compiler errors recently which boils down into the following dummy example. Basically I am building a "plus2" function template, which I want it to work only on int and float. Logically the program only adds 2 when the "is_numerical" type trait test goes through. However it hangs at compiling with error C2782/C2784/C2676 complaining about adding 2 to a string.
The example is for illustration only and does not make sense. More importantly, what would be the correct way to code this kind of logic up? Thanks.
#include <iostream>
#include <string>
using namespace std;
template <typename T>
struct is_numerical {
static const bool value = false;
};
template <>
struct is_numerical<float> {
static const bool value = true;
};
template <>
struct is_numerical<int> {
static const bool value = true;
};
template <typename T>
T plus2(T input) {
if (is_numerical<T>::value) {
return input + 2;
} else { return input; }
}
int main()
{
//char x('a'); // prints 'a'
string x("a"); // compiler error
cout << plus2(x) << endl;
return 0;
}
The problem is that when T input is a std::string, you're still trying to compile return input + 2;. Even though it's in an if statement that's always false.
In C++17, if constexpr allows conditionally-compiled code.
template <typename T>
T plus2(T input) {
if constexpr (is_numerical<T>::value) {
return input + 2;
} else { return input; }
}
In all standardized versions of C++, SFINAE can also prevent the invalid code from ever being compiled.
template <typename T>
T plus2(T input, typename std::enable_if<! is_numerical<T>::value>::type* = 0) {
return input;
}
template <typename T>
T plus2(T input, typename std::enable_if<is_numerical<T>::value>::type* = 0) {
return input + 2;
}
You need to make tests like this part of the function signature rather than the body.,If the function signature matches the compiler is going to try and instantiate it but at that point it fails.
template<typename T>
T plus#2( value, typename std::enable_if<is_numeric<T>::value>::type * = nullptr)
{
return value + 2;
}
Note that if you then tried passing something for which the test fails you will get a "no matching signature" type error.
Usually when you write a CLI tool which accepts parameter you have to deal with them. Most of the time you want to switch between behaviours based on the value of an argument.
The following is a common use case, where the program accepts a type and then prints something based on that type. I am using Boost to pre-process and auto generate the whole if-else branches.
This is very nice in terms of maintainability as I only need to update a define when I introduce a new type. On the other hand it is quite far from being modern and elegant.
I thought about using better-enums to avoid using the if-else to convert from string into an enum using the _from_string utility function. But then the way to go from enum to a type is still obscure to me.
Any suggestion on how to keep the nice maintainability of the current implementation but avoid to use pre-processor and macro functionalities?
#include <iostream>
#include <cstdlib>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <type_traits>
using a_type = int;
using b_type = long;
using c_type = float;
using d_type = double;
#define TYPES (a)(b)(c)(d)
template<typename T>
void foo(){
T num = 1;
std::cout << typeid(decltype(num)).name() << " : "<< num << std::endl;
};
int main(int argc, char **argv)
{
if (argc < 1) {
return 1;
}
std::string type = argv[1];
if (false) {
#define LOOP_BODY(R, DATA, T) \
} \
else if (type == BOOST_PP_STRINGIZE(T)) { \
foo<BOOST_PP_CAT(T, _type)>(); \
BOOST_PP_SEQ_FOR_EACH(LOOP_BODY, _, TYPES);
#undef LOOP_BODY
} else {
std::cout << "ERROR: Unknown type " << type << std::endl;
}
}
Working example at https://wandbox.org/permlink/60bAwoqYxzU1EUdw
Another way is to use a plain array and std::find_if instead of if-else:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
#include <typeinfo>
struct Handler {
char const* name;
void(*fn)(std::string const&); // Or std::function<> to accept lambdas.
};
struct A {};
struct B {};
template<class T>
void foo(std::string const& name) {
std::cout << "foo<" << typeid(T).name() << ">: " << name << '\n';
}
int main(int, char** av) {
Handler const handlers[] = {
{"a", foo<A>}
, {"b", foo<B>}
};
std::string const name = av[1];
auto handler = std::find_if(std::begin(handlers), std::end(handlers), [&name](auto const& h) {
return name == h.name;
});
if(handler != std::end(handlers))
handler->fn(name);
}
You don't need to use the preprocessor to store an arbitrary list of types and generate code for them. We can use variadic templates and compile-time strings. You can isolate preprocessor usage to the generation of pairs of names and types.
Firstly, let's define a wrapper for a compile-time sequence of characters. Note that the use of the _cs literal is non-Standard, but available in every major compiler and likely to be part of C++20:
template <char... Cs>
using ct_str = std::integer_sequence<char, Cs...>;
template <typename T, T... Cs>
constexpr ct_str<Cs...> operator""_cs() { return {}; }
We can then define an empty type that stores a pair of a name and a type:
template <typename Name, typename T>
struct named_type
{
using name = Name;
using type = T;
};
And a macro to conveniently instantiate it:
#define NAMED_TYPE(type) \
named_type<decltype(#type ## _cs), type>
You can now use an empty variadic template class to store your types:
template <typename... Ts>
struct named_type_list { };
using my_types = named_type_list<
NAMED_TYPE(int),
NAMED_TYPE(long),
NAMED_TYPE(float),
NAMED_TYPE(double)
>;
Now, let's see how our main should look:
int main()
{
const std::string input{"float"};
handle(my_types{}, input, [](auto t)
{
print(typename decltype(t)::name{});
});
}
The above will print out "float". We can implement handle by unpacking the list of named_type types and using a fold expression to find the matching type name:
template <typename... Ts, typename F>
void handle(named_type_list<Ts...>, const std::string& input, F&& f)
{
( (same(input, typename Ts::name{}) && (f(Ts{}), true) ) || ...);
}
Checking for equality between std::string and ct_str is annoying, but doable:
template <std::size_t... Is, char... Cs>
bool same_impl(const std::string& s,
std::integer_sequence<char, Cs...>,
std::index_sequence<Is...>)
{
return ((s[Is] == Cs) && ...);
}
template <char... Cs>
bool same(const std::string& s, std::integer_sequence<char, Cs...> seq)
{
return s.size() >= sizeof...(Cs)
&& same_impl(s, seq, std::make_index_sequence<sizeof...(Cs)>{});
}
final result live on wandbox.org
Note that this answer uses C++17 fold expressions. You can replace them in C++14 with one of the following techniques:
Recursive variadic template function, where the base case returns the default accumulation value, and the recursive case performs an operation between the tail and the head.
C++11 pack expansion tricks such as for_each_argument.
The dispatching does short-circuit:
( (same(input, typename Ts::name{}) && (f(Ts{}), true) ) || ...);
This fold expression will stop at the first invocation of f thanks to the , true expression and the || operator.
empirical proof on wandbox.org
I am attempting to create generic parser-elements using qi as I unfortunately (MSVC must be supported) can not use X3.
The idea is to have a templated struct:
template<class T> struct parse_type;
Which I could use like this:
template<class T> T from_string(std::string const& s)
{
T res;
parse_type<T> t;
...
if (phrase_parse(...,parse_type<T>(),...,t))
}
or specialise like this
template<class T,class Alloc>
struct parse_type<std::vector<T,Alloc>>
{
// Parse a vector using rule '[' >> parse_type<T> % ',' > ']';
}
The primary purpose is to allow for easy parsing of e.g. std::tuple, boost::optional and boost::variant (The last one can not be automatic due to the greedy nature of qi).
I would appreciate feedback as to how approach this. Currently I base my struct on qi::grammar, but grammar is not supported in X3 and I would like to use X3 when MSVC compiles this, and I am also a little bit uncomfortable with having to provide the skipper.
An alternative would be to have a static function in parse_type that returns the appropriate rule. I am considering if this is a cleaner approach?
Any feedback will be appreciated.
Update2: Replaced code-snippet with compilable example that fails at runtime. Here is the code:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
#include <string>
#include <iostream>
#include <iostream>
// Support to simplify
using iter = std::string::const_iterator;
void print(std::vector<int> const& v)
{
std::cout << '[';
for (auto i: v) std::cout << i << ',';
std::cout << "]";
}
namespace qi = boost::spirit::qi;
// My rule factory - quite useless if you do not specialise
template<class T> struct ps_rule;
// An example of using the factory
template<class T>
T from_string(std::string const& s)
{
T result;
iter first { std::begin(s) };
auto rule = ps_rule<T>::get();
phrase_parse(first,std::end(s),rule,qi::space,result);
return result;
}
// Specialising rule for int
template<>
struct ps_rule<int>
{
static qi::rule<iter,int()> get() { return qi::int_; }
};
// ... and for std::vector (where the elements must have rules)
template<class T,class Alloc>
struct ps_rule<std::vector<T,Alloc>>
{
static qi::rule<iter,std::vector<T,Alloc>()> get()
{
qi::rule<iter,std::vector<T,Alloc>()> res;
res.name("Vector");
res =
qi::lit('{')
>> ps_rule<T>::get() % ','
>> '}';
return res;
}
};
int main()
{
// This one works like a charm.
std::cout << ((from_string<int>("100") == 100) ? "OK\n":"Failed\n");
std::vector<int> v {1,2,3,4,5,6};
// This one fails
std::cout << ((from_string<std::vector<int>>("{1,2,3,4,5,6}") == v) ? "OK\n":"Failed\n");
}
The code fails in boost/function_template.hpp line 766:
result_type operator()(BOOST_FUNCTION_PARMS) const
{
if (this->empty())
boost::throw_exception(bad_function_call());
return get_vtable()->invoker
(this->functor BOOST_FUNCTION_COMMA BOOST_FUNCTION_ARGS);
}
This code is a member function in boost::function4
,boost::fusion::vector0 > &
,boost::spirit::unused_type const&>
and the problem is that get_vtable returns an invalid pointer.
Your main problem is that the copy constructor for qi::rule takes a reference to the original rule, which in your case is a local variable. One way you can avoid this problem is by using qi::rule's copy member function but this requires changing slightly the return type of your specialization of ps_rule.
static typename boost::proto::terminal<qi::rule<iter,std::vector<T,Alloc>()>>::type get()
{
//[...] (same as before)
return res.copy();
}
Once you do that, the same problem arises with your ps_rule<int> even though it seemed to work in isolation. You could do something analogous but in this case the rule is not required, it would be better (even from a performance point of view) to just use something like:
static qi::int_type get() { return qi::int_; }
Full Sample (Running on WandBox)
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
// Support to simplify
using iter = std::string::const_iterator;
void print(std::vector<int> const& v)
{
std::cout << '[';
for (auto i: v) std::cout << i << ',';
std::cout << "]";
}
namespace qi = boost::spirit::qi;
// My rule factory - quite useless if you do not specialise
template<class T> struct ps_rule;
// An example of using the factory
template<class T>
T from_string(std::string const& s)
{
T result;
iter first { std::begin(s) };
auto rule = ps_rule<T>::get();
qi::phrase_parse(first,std::end(s),rule,qi::space,result);
return result;
}
// Specialising rule for int
template<>
struct ps_rule<int>
{
static qi::int_type get() { return qi::int_; }
};
// ... and for std::vector (where the elements must have rules)
template<class T,class Alloc>
struct ps_rule<std::vector<T,Alloc>>
{
static typename boost::proto::terminal<qi::rule<iter,std::vector<T,Alloc>()>>::type get()
{
qi::rule<iter,std::vector<T,Alloc>()> res;
res.name("Vector");
res =
qi::lit('{')
>> ps_rule<T>::get() % ','
>> '}';
return res.copy();
}
};
int main()
{
// This one works like a charm.
std::cout << ((from_string<int>("100") == 100) ? "OK\n":"Failed\n");
std::vector<int> v {1,2,3,4,5,6};
std::cout << ((from_string<std::vector<int>>("{1,2,3,4,5,6}") == v) ? "OK\n":"Failed\n");
std::vector<std::vector<int> > vv {{1,2,3},{4,5,6}};
std::cout << ((from_string<std::vector<std::vector<int>>>("{{1,2,3},{4,5,6}}") == vv) ? "OK\n":"Failed\n");
}
PS: You can save lots of specializations if you use Spirit's own machinery to create parsers automatically in your primary template. Here is an example.
I want to generate some formatted output. For this some indention is needed. So at some point during generation I would like to get the current position, to have the following lines indented with that amount.
Here is a minimal example. Please assume, that we don't know how long the output of karma::lit("Some text: ") is during compile time. In fact, this leading text may be generated by several rules.
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
#include <boost/spirit/include/karma.hpp>
using namespace std;
int main() {
vector<int> v { 0, 1, 2, 3 };
{
namespace karma = boost::spirit::karma;
karma::rule<ostream_iterator<char>, std::vector<int>() > myRule =
karma::lit("Some text: ") << (karma::int_ % karma::eol);
karma::generate(ostream_iterator<char>(cout), myRule, v);
}
return 0;
}
This produces
Some text: 0
1
2
3
I would like the result:
Some text: 0
1
2
3
To achieve this, one needs to know the current position, right before the vector gets generated. So, something like an equivalent for qi::raw[]?
Update: A pointer to the up to this point generated output, would also do.
I believe this approach is similar to the one you described in the comments. It assumes that the only information you can get from the iterator is the total count of characters written. It could be simplified further if you had access to the current column by modifying the header files as mentioned in the other answer.
Edit: Modified the code with the approach Mike M suggested in the comments. Now it has a better interface. Tested with g++ 4.8.1 and clang 3.2 using boost 1.54.0.
In order to use you need to first define two terminals of type position_getter:
std::size_t start=0, end=0;
position_getter start_(start), end_(end);
Then you simply put start_ at the start of a line, and end_ at the point where you want to know in which column you are. After that you can use end - start to calculate that column. Since this calculation needs to be done at parse time (not compile time) you need to use phx::ref(end) - phx::ref(start).
With the modifications mentioned in the other answer, you could simply define one terminal:
std::size_t column=0;
position_getter column_(column);
And then use it in rule like this:
myRule = karma::lit("Some text: ")
<< column_
<< karma::int_ %
(karma::eol << karma::repeat(phx::ref(column))[karma::char_(" ")]);
#include <iostream>
#include <string>
#include <vector>
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
//START OF CURRENT_POS.HPP
#include <boost/spirit/include/karma_generate.hpp>
///////////////////////////////////////////////////////////////////////////////
// definition the place holder
namespace custom_generator {
BOOST_SPIRIT_TERMINAL_EX(current_pos);
struct position_getter: boost::spirit::terminal<
boost::spirit::tag::stateful_tag<std::size_t&, tag::current_pos> > {
typedef boost::spirit::tag::stateful_tag<std::size_t&, tag::current_pos> tag_type;
position_getter(std::size_t& p)
: boost::spirit::terminal<tag_type>(p) {
}
};
}
///////////////////////////////////////////////////////////////////////////////
// implementation the enabler
namespace boost {
namespace spirit {
// enables a terminal of type position_getter
template<>
struct use_terminal<karma::domain,
tag::stateful_tag<std::size_t&, custom_generator::tag::current_pos> > : mpl::true_ {
};
}
}
///////////////////////////////////////////////////////////////////////////////
// implementation of the generator
namespace custom_generator {
struct current_pos_generator: boost::spirit::karma::primitive_generator<
current_pos_generator> {
current_pos_generator(std::size_t& pos_)
: pos(pos_) {
}
// Define required output iterator properties
typedef typename boost::mpl::int_<
boost::spirit::karma::generator_properties::tracking> properties;
// Define the attribute type exposed by this parser component
template<typename Context, typename Unused>
struct attribute {
typedef boost::spirit::unused_type type;
};
// This function is called during the actual output generation process.
// It stores information about the position in the output stream in
// the variable you used to construct position_getter
template<typename OutputIterator, typename Context, typename Delimiter,
typename Attribute>
bool generate(OutputIterator& sink, Context& ctx,
Delimiter const& delimiter, Attribute const& attr) const {
std::size_t column = sink.get_out_count();
// This would only work if you comment "private:" in line 82 of
// boost/spirit/home/karma/detail/output_iterator.hpp
// std::size_t column = sink.track_position_data.get_column()-1;
pos = column;
return true;
}
// This function is called during error handling to create
// a human readable string for the error context.
template<typename Context>
boost::spirit::info what(Context& ctx) const {
return boost::spirit::info("current_pos");
}
std::size_t& pos;
};
}
///////////////////////////////////////////////////////////////////////////////
// instantiation of the generator
namespace boost {
namespace spirit {
namespace karma {
template<typename Modifiers>
struct make_primitive<
tag::stateful_tag<std::size_t&, custom_generator::tag::current_pos>,
Modifiers> {
typedef custom_generator::current_pos_generator result_type;
template<typename Terminal>
result_type operator()(Terminal& term, unused_type) const {
typedef tag::stateful_tag<std::size_t&,
custom_generator::tag::current_pos> tag_type;
using spirit::detail::get_stateful_data;
return result_type(get_stateful_data<tag_type>::call(term));
}
};
}
}
}
//END OF CURRENT_POS.HPP
int main() {
std::vector<int> v { 0, 1, 2, 3 };
{
namespace karma = boost::spirit::karma;
namespace phx = boost::phoenix;
using custom_generator::position_getter;
std::size_t start = 0, end = 0;
position_getter start_(start), end_(end);
karma::rule<std::ostream_iterator<char>, std::vector<int>()> myRule =
start_
<< karma::lit("Some text: ")
<< end_
<< karma::int_ % (karma::eol
<< karma::repeat(phx::ref(end) - phx::ref(start))[karma::char_(
" ")]);
karma::generate(std::ostream_iterator<char>(std::cout), myRule, v);
std::cout << std::endl;
karma::rule<std::ostream_iterator<char>, std::vector<int>()> myRuleThatAlsoWorks =
karma::lit(":)")
<< karma::eol
<< start_
<< karma::lit("Some text: ")
<< end_
<< karma::int_ % (karma::eol
<< karma::repeat(phx::ref(end) - phx::ref(start))[karma::char_(
" ")]);
karma::generate(std::ostream_iterator<char>(std::cout), myRuleThatAlsoWorks,
v);
}
return 0;
}
Here is a custom directive heavily based on the explanations here.
Unfortunately due to the fact that the information you need is contained in a private member of the iterator, this only works with the really simple example that you posted. If you output anything else before everythings gets misaligned. You can work around this if you are willing to modify slightly the code in detail/output_iterator.hpp. You can either comment the "private:" in position_policy or simply add a member function get_out_column in the same vein as get_out_count.
In order to use it you need to change your:
karma::int_ % karma::eol;
to:
custom_generator::align_list_to_current_position[karma::int_];
As you can see the custom directive requires a lot of boilerplate but big part of this code is common to every directive. In fact, besides changing the names, I have only needed to change three things:
Make sure that tracking is in the set of required properties:
typedef typename boost::mpl::int_<
Subject::properties::value | karma::generator_properties::tracking
> properties;
Make the attribute of the directive be the same as the one a list(%) would have(by looking here):
template <typename Context, typename Iterator>
struct attribute
: boost::spirit::traits::build_std_vector<
typename boost::spirit::traits::attribute_of<Subject, Context, Iterator>::type
>
{};
And finally change the generate function. In this function I simply build a list that has as its left member whatever you passed to the directive and as its right one the concatenation of karma::eol and as many spaces are as needed to be aligned.
#include <iostream>
#include <string>
#include <vector>
#include <boost/spirit/include/karma.hpp>
//START OF ALIGN_LIST_TO_CURRENT_POSITION.HPP
#include <boost/spirit/include/karma_generate.hpp>
///////////////////////////////////////////////////////////////////////////////
// definition the place holder
namespace custom_generator
{
BOOST_SPIRIT_TERMINAL(align_list_to_current_position);
}
///////////////////////////////////////////////////////////////////////////////
// implementation the enabler
namespace boost { namespace spirit
{
// We want custom_generator::align_list_to_current_position to be usable as a directive only,
// and only for generator expressions (karma::domain).
template <>
struct use_directive<karma::domain, custom_generator::tag::align_list_to_current_position>
: mpl::true_ {};
}}
///////////////////////////////////////////////////////////////////////////////
// implementation of the generator
namespace custom_generator
{
// That's the actual columns generator
template <typename Subject>
struct align_list_to_current_position_generator
: boost::spirit::karma::unary_generator<
align_list_to_current_position_generator<Subject> >
{
// Define required output iterator properties: take the properties needed by the subject and add tracking
typedef typename boost::mpl::int_<Subject::properties::value | boost::spirit::karma::generator_properties::tracking> properties;
// Define the attribute type exposed by this parser component
template <typename Context, typename Iterator>
struct attribute
: boost::spirit::traits::build_std_vector<
typename boost::spirit::traits::attribute_of<Subject, Context, Iterator>::type>
{};
align_list_to_current_position_generator(Subject const& s)
: subject(s)
{}
// This function is called during the actual output generation process.
// It dispatches to the embedded generator while supplying a new
// delimiter to use
template <typename OutputIterator, typename Context
, typename Delimiter, typename Attribute>
bool generate(OutputIterator& sink, Context& ctx
, Delimiter const& delimiter, Attribute const& attr) const
{
using boost::spirit::karma::repeat;
using boost::spirit::karma::char_;
using boost::spirit::karma::eol;
using boost::spirit::karma::domain;
std::size_t column = sink.get_out_count();
//This would only work if you comment "private:" in line 82 of boost/spirit/home/karma/detail/output_iterator.hpp
// std::size_t column = sink.track_position_data.get_column()-1;
return boost::spirit::compile<domain>(subject%(eol << repeat(column)[char_(" ")])).generate(sink, ctx, delimiter, attr);
}
// This function is called during error handling to create
// a human readable string for the error context.
template <typename Context>
boost::spirit::info what(Context& ctx) const
{
return boost::spirit::info("align_list_to_current_position", subject.what(ctx));
}
Subject subject;
};
}
///////////////////////////////////////////////////////////////////////////////
// instantiation of the generator
namespace boost { namespace spirit { namespace karma
{
// This is the factory function object invoked in order to create
// an instance of our align_list_to_current_position_generator.
template <typename Subject, typename Modifiers>
struct make_directive<custom_generator::tag::align_list_to_current_position, Subject, Modifiers>
{
typedef custom_generator::align_list_to_current_position_generator<Subject> result_type;
result_type operator()(unused_type, Subject const& s, unused_type) const
{
return result_type(s);
}
};
}}}
//END OF ALIGN_LIST_TO_CURRENT_POSITION.HPP
int main() {
std::vector<int> v { 0, 1, 2, 3 };
{
namespace karma = boost::spirit::karma;
using custom_generator::align_list_to_current_position;
karma::rule<std::ostream_iterator<char>, std::vector<int>() > myRule =
karma::lit("Some text: ") << align_list_to_current_position[karma::int_];
karma::generate(std::ostream_iterator<char>(std::cout), myRule, v);
std::cout << std::endl;
//This rule would work if you make the changes mentioned in align_list_to_current_position_generator::generate
karma::rule<std::ostream_iterator<char>, std::vector<int>() > myRuleThatFails =
karma::lit(":_(") << karma::eol << karma::lit("Some text: ") << align_list_to_current_position[karma::int_ << karma::int_];
karma::generate(std::ostream_iterator<char>(std::cout), myRuleThatFails, v);
}
return 0;
}
I have a function that scans the user's file system, fills a vector with the paths, then either sorts it or not. Since the user should be able to decide at compile-time whether he wants the vector sorted or not, I use templates and helper classes in place of a much desired (but not existing) "static if".
Consider this code:
enum class Sort{Alphabetic, Unsorted};
template<Sort TS> struct SortHelper;
template<> struct SortHelper<Sort::Alphabetic>
{
static void sort(vector<string>& mTarget) { sort(begin(mTarget), end(mTarget)); }
};
template<> struct SortHelper<Sort::Unsorted>
{
static void sort(vector<string>&) { }
};
template<Sort TS> struct DoSomethingHelper
{
static void(vector<string>& mTarget)
{
// do something with mTarget
SortHelper<TS>::sort(mTarget);
}
};
The code I've written above is GREATLY simplified from the original, which takes multiple template parameters to allow the user to customize even further the results of the function at compile-time.
Is there an alternative to using all of these helper classes? It gets really messy and hard to read.
Ideally, this is what I would like to write:
enum class Sort{Alphabetic, Unsorted};
template<Sort TS> struct DoSomethingHelper
{
static void(vector<string>& mTarget)
{
// do something with mTarget
static_if(TS == Sort::Unsorted) { /* do nothing */ }
static_if(TS == Sort::Alphabetic) { sort(begin(mTarget), end(mTarget)); }
}
};
Since your value is known at compile time (non-template type parameter) you can perfectly write a "normal" if:
template<Sort TS>
void someFunction(vector<string>& mTarget)
{
if (TS == Sort::Alphabetic) { sort(begin(mTarget), end(mTarget)); }
// else if (TS == Sort::Unsorted) {}
}
The compiler will perform constant folding and dead code elimination (if those optimisations are enabled, of course), and the result will be exactly the same as if you used the hypothetical static_if.
I am afraid there has been a misunderstanding about the usage of static_if.
Certainly you can use static_if (or whatever trick you wish really) to try and get some optimization, but that is not its first goal.
The first goal of static_if is semantical. Let me demonstrate this with std::advance. A typical implementation of std::advance will use a type switch to choose, at compile time, between an O(1) implementation (for Random Access Iterators) and an O(n) implementation (for the others):
template <typename It, typename D>
void advance_impl(It& it, D d, random_access_iterator_tag)
{
it += d;
}
template <typename It, typename D>
void advance_impl(It& it, D d, bidirectional_iterator_tag)
{
if (d > D(0)) { for (D i(0); i < d; ++i) { ++it; } }
else { for (D i(0); i > d; --i) { --it; } }
}
template <typename It, typename D>
void advance_impl(It& it, D d, input_iterator_tag)
{
for (D i(0); i < d; ++i) { ++it; }
}
And finally:
template <typename It, typename D>
void advance(It& it, D d)
{
typename std::iterator_traits<It>::iterator_category c;
advance_impl(it, d, c);
}
Why not use just a if in this case ? Because it would not compile.
a Bidirectional Iterator does not support +=
an Input Iterator (or Forward Iterator) does not support --
Thus, the only way to implement the functionality is to statically dispatch to a function only using the available operations on the given type.
What about template specialization?
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
enum class Sort {
Alphabetic,
Unsorted
};
template<Sort TS> struct DoSomethingHelper {
static void someFunction(vector<string>& mTarget)
{}
};
template<> struct DoSomethingHelper<Sort::Unsorted> {
static void someFunction(vector<string>& mTarget) {
}
};
template<> struct DoSomethingHelper<Sort::Alphabetic> {
static void someFunction(vector<string>& mTarget) {
sort(begin(mTarget), end(mTarget));
}
};
int main() {
vector<string> v = {{"foo", "bar", "foo2", "superman", ".."}};
DoSomethingHelper<Sort::Alphabetic> helper;
helper.someFunction(v);
for (string& s : v) {
cout << s << endl;
}
return 0;
}
Edit: I'm a idiot.