Related
Suppose we have a data structure that is a key-value map, where the key itself is again a key-value map. For example:
map<map<string,string>>, string>
Now, suppose that we want to query all top-level key/values in this map matching a certain subset of the key-values of the key. Example:
map = { { "k1" : "v1", "k2 : "v2" } : "value1",
{ "k1" : "v3", "k2 : "v4" } : "value2",
{ "k1" : "v1", "k2 : "v5" } : "value3"
}
And our query is "give me all key-values where key contains { "k1" : "v1" } and it would return the first and third value. Similarly, querying for { "k1" : "v3", "k2" : "v4" } would return all key-values that have both k1=v3 and k2=v4, yielding the second value. Obviously we could search through the full map on every query, but I'm looking for something more efficient than that.
I have looked around, but can't find an efficient, easy-to-use solution out there for C++. Boost multi_index does not seem to have this kind of flexibility in querying subsets of key-value pairs.
Some databases have ways to create indices that can answer exactly these kind of queries. For example, Postgres has GIN indices (generalized inverted indices) that allow you to ask
SELECT * FROM table WHERE some_json_column #> '{"k1":"v1","k2":"v2"}'
-- returns all rows that have both k1=v1 and k2=v2
However, I'm looking for a solution without databases just in C++. Is there any library or data structure out there that can accomplish something like this? In case there is none, some pointers on a custom implementation?
I would stay with the database index analogy. In that analogy, the indexed search does not use a generic k=v type search, but just a tuple with the values for the elements (generally columns) that constitute the index. The database then reverts to scans for the other k=v parameters that are not in the index.
In that analogy, you would have a fixed number of keys that could be represented as an array or strings (fixed size). The good news is that it is then trivial to set a global order on the keys, and thanks to the std::map::upper_bound method, it is also trivial to find an iterator immediately after a partial key.
So getting a full key is immediate: just extract it with find, at or operator []. And getting all elements for a partial key is still simple:
find an iterator starting above the partial key with upper_bound
iterate forward while the element matches the partial key
But this require that you change your initial type to std::map<std::array<string, N>, string>
You could build an API over this container using std::map<string, string> as input values, extract the actual full or partial key from that, and iterate as above, keeping only elements matching the k,v pairs not present in index.
You could use std::includes to check if key maps include another map of queried key-value pairs.
I am unsure how to avoid checking every key-map though. Maybe other answers have a better idea.
template <typename MapOfMapsIt, typename QueryMapIt>
std::vector<MapOfMapsIt> query_keymap_contains(
MapOfMapsIt mom_fst,
MapOfMapsIt mom_lst,
QueryMapIt q_fst,
QueryMapIt q_lst)
{
std::vector<MapOfMapsIt> out;
for(; mom_fst != mom_lst; ++mom_fst)
{
const auto key_map = mom_fst->first;
if(std::includes(key_map.begin(), key_map.end(), q_fst, q_lst))
out.push_back(mom_fst);
}
return out;
}
Usage:
typedef std::map<std::string, std::string> StrMap;
typedef std::map<StrMap, std::string> MapKeyMaps;
MapKeyMaps m = {{{{"k1", "v1"}, {"k2", "v2"}}, "value1"},
{{{"k1", "v3"}, {"k2", "v4"}}, "value2"},
{{{"k1", "v1"}, {"k2", "v5"}}, "value3"}};
StrMap q1 = {{"k1", "v1"}};
StrMap q2 = {{"k1", "v3"}, {"k2", "v4"}};
auto res1 = query_keymap_contains(m.begin(), m.end(), q1.begin(), q1.end());
auto res2 = query_keymap_contains(m.begin(), m.end(), q2.begin(), q2.end());
std::cout << "Query1: ";
for(auto i : res1) std::cout << i->second << " ";
std::cout << "\nQuery2: ";
for(auto i : res2) std::cout << i->second << " ";
Output:
Query1: value1 value3
Query2: value2
Live Example
I believe the efficiency of different methods will depend on actual data. However, I would consider making a "cache" of iterators to outer map elements for particular "kX","vY" pairs as follows:
using M = std::map<std::map<std::string, std::string>, std::string>;
M m = {
{ { { "k1", "v1" }, { "k2", "v2" } }, "value1" },
{ { { "k1", "v3" }, { "k2", "v4" } }, "value2" },
{ { { "k1", "v1" }, { "k2", "v5" } }, "value3" }
};
std::map<M::key_type::value_type, std::vector<M::iterator>> cache;
for (auto it = m.begin(); it != m.end(); ++it)
for (const auto& kv : it->first)
cache[kv].push_back(it);
Now, you basically need to take all searched "kX","vY" pairs and find the intersection of cached iterators for them:
std::vector<M::key_type::value_type> find_list = { { "k1", "v1" }, { "k2", "v5" } };
std::vector<M::iterator> found;
if (find_list.size() > 0) {
auto it = find_list.begin();
std::copy(cache[*it].begin(), cache[*it].end(), std::back_inserter(found));
while (++it != find_list.end()) {
const auto& temp = cache[*it];
found.erase(std::remove_if(found.begin(), found.end(),
[&temp](const auto& e){ return std::find(temp.begin(), temp.end(), e) == temp.end(); } ),
found.end());
}
}
The final output:
for (const auto& it : found)
std::cout << it->second << std::endl;
gives value3 in this case.
A live demo: https://wandbox.org/permlink/S9Zp8yofSvjfLokc.
Note that the complexity of the intersection step is quite large, since cached iterators are unsorted. If you use pointers instead, you can sort the vectors or store the pointers in a map instead, which would allow you to find intersections much faster, e.g., by using std::set_intersection.
You can do it with as single (partial) pass through each element with an ordered query, returning early as much as possible. Taking inspiration from std::set_difference, we want to know if query is a subset of data, which lets us select entries of the outer map.
// Is the sorted range [first1, last1) a subset of the sorted range [first2, last2)
template<class InputIt1, class InputIt2>
bool is_subset(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
{
while (first1 != last1) {
if (first2 == last2) return false; // Reached the end of data with query still remaing
if (*first1 < *first2) {
return false; // didn't find this query element
} else {
if (! (*first2 < *first1)) {
++first1; // found this query element
}
++first2;
}
}
return true; // reached the end of query
}
// find every element of "map-of-maps" [first2, last2) for which the sorted range [first1, last1) is a subset of it's key
template<class InputIt1, class InputIt2, class OutputIt>
OutputIt query_data(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt d_first)
{
auto item_matches = [=](auto & inner){ return is_subset(first1, last1, inner.first.begin(), inner.first.end()); };
return std::copy_if(first2, last2, d_first, item_matches);
}
std::map is implemented as a balanced binary tree which has O(nlgn) look-up. What you need instead, is std::unordered_map which is implemented as a hash-table, that is O(1) look-ups.
Now let me rephrase your wording, you want to:
And our query is "give me all key-values where key contains { "k1" : "v1" } and it would return the first and third value.
Which translates to:
If the key-value pair given is in the inner map, give me back its value.
Essentially what you need is a double look-up which std::unordered_map excel at.
Here is a code spinet that solves your problem with the standard library (no fancy code required)
#include <iostream>
#include <unordered_map>
#include <string>
int main() {
using elemType = std::pair<std::string, std::string>;
using innerMap = std::unordered_map<std::string, std::string>;
using myMap = std::unordered_map<std::string, innerMap>;
auto table = myMap{ { "value1", { {"k1", "v1"}, {"k2", "v2"} } },
{ "value2", { {"k1", "v3"}, {"k2", "v4"} } },
{ "value3", { {"k1", "v1"}, {"k2", "v5"} } } };
//First we set-up a predicate lambda
auto printIfKeyValueFound = [](const myMap& tab, const elemType& query) {
// O(n) for the first table and O(1) lookup for each, O(n) total
for(const auto& el : tab) {
auto it = el.second.find(query.first);
if(it != el.second.end()) {
if(it->second == query.second) {
std::cout << "Element found: " << el.first << "\n";
}
}
}
};
auto query = elemType{"k1", "v1"};
printIfKeyValueFound(table, query);
Output: Value3, Value1
For queries of arbitrary size you can:
//First we set-up a predicate lambda
auto printIfKeyValueFound = [](const myMap& tab, const std::vector<elemType>& query) {
// O(n) for the first table and O(n) for the query O(1) search
// O(n^2) total
for(const auto& el : tab) {
bool found = true;
for(const auto& queryEl : query) {
auto it = el.second.find(queryEl.first);
if(it != el.second.end() && it->second != queryEl.second) {
found = false;
break;
}
}
if(found)
std::cout << el.first << "\n";
}
};
auto query = std::vector<elemType>{ {"k1", "v1"}, {"k2", "v2"} };
output Value1
The following code groups values for any container with a generic grouping lambda:
template<class Iterator, class GroupingFunc,
class T = remove_reference_t<decltype(*declval<Iterator>())>,
class GroupingType = decltype(declval<GroupingFunc>()(declval<T&>()))>
auto groupValues(Iterator begin, Iterator end, GroupingFunc groupingFunc) {
map<GroupingType, list<T>> groups;
for_each(begin, end,
[&groups, groupingFunc](const auto& val){
groups[groupingFunc(val)].push_back(val);
} );
return groups;
}
With the following usage:
int main() {
list<string> strs = {"hello", "world", "Hello", "World"};
auto groupOfStrings =
groupValues(strs.begin(), strs.end(),
[](auto& val) {
return (char)toupper(val.at(0));
});
print(groupOfStrings); // assume a print method
list<int> numbers = {1, 5, 10, 24, 13};
auto groupOfNumbers =
groupValues(numbers.begin(), numbers.end(),
[](int val) {
int decile = int(val / 10) * 10;
return to_string(decile) + '-' + to_string(decile + 9);
});
print(groupOfNumbers); // assume a print method
}
I am a bit reluctant regarding the (over?)-use of declval and decltype in groupValues function.
Do you see a better way for writing it?
(Question is mainly for better style and clarity unless of course you see any other issue).
Code: http://coliru.stacked-crooked.com/a/f65d4939b402a750
I would probably move the last two template parameters inside the function, and use std::result_of to give a slightly more tidy function:
template <typename T>
using deref_iter_t = std::remove_reference_t<decltype(*std::declval<T>())>;
template<class Iterator, class GroupingFunc>
auto groupValues(Iterator begin, Iterator end, GroupingFunc groupingFunc) {
using T = deref_iter_t<Iterator>;
using GroupingType = std::result_of_t<GroupingFunc(T&)>;
std::map<GroupingType, std::list<T>> groups;
std::for_each(begin, end, [&groups, groupingFunc](const auto& val){
groups[groupingFunc(val)].push_back(val);
});
return groups;
}
live demo
Having defined my objects myType, I need to store relations between these objects. These relations are stored on a matrix.
The number of elements is not known in advance, not all elements have a relation (element1 can have a relation with element3, but may not have one with 5) and memory is an issue. For example it could look like:
element45 is connected with:
element3 with characteristic [3,1;1,4]
element12 with characteristic [1,1;1,1]
element1780 with characteristic [8,1;1,4]
element1661 is connected with:
element3 with characteristic [3,1;6,4]
element1 with characteristic [1,1;1,9]
element1780 with characteristic [8,1;1,1]
Having:
myType* element1;
myType* element2;
I would like to have something like (properly pointed the elements):
my_table[element1][element2][1][2]=7;
I have thought on creating a nested hash table using boost library:
boost::unordered_map<myType*, boost::unordered_map<myType*,
std::vector<std::vector <unsigned short int> > > > my_table;
However, even if the code compiles, it crashes (Segmentation fault, it points to a line calculating the hash key) running a simple line like:
my_table[element1][element2].resize(2);
for(int t=0; t<2; ++t)
my_table[element1][element2][t].resize(2);
Anyone can give me some light about this? Is this practically or conceptually wrong?
Any other approach to this problem is welcome.
Thank you
Right off the bat it seems obvious to me that your datastructure represent a graph (with attributed vertices and edges connecting them).
Furthermore when you say "These relations are stored on a matrix." you apparently mean "I visualize this as a matrix", since a true matrix representation¹ would become horrifically space-inefficient for larger number of vertices and sparse edge coverage.
Boost has a library for that: Boost Graph Library (BGL)
If we assume you want to be able to read a graph like²
graph X {
element1; element12; element166; element1780; element3; element4;
element45 -- element3 [ label="[3,1;1,4]" ];
element45 -- element12 [ label="[1,1;1,1]" ];
element45 -- element1780 [ label="[8,1;1,4]" ];
element1661 -- element1 [ label="[1,1;1,9]" ];
element1661 -- element3 [ label="[3,1;6,4]" ];
element1661 -- element1780 [ label="[8,1;1,1]" ];
}
Into a BGL compatible model, use typedefs like e.g.:
struct Vertex {
std::string node_id;
};
struct Edge {
Box box;
};
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Vertex, Edge>;
Now you leverage the full facilities of the BGL:
Reading the graph from a file
Reading from a graphviz file is a feature³:
std::ifstream ifs("input.txt");
Graph result;
boost::dynamic_properties dp;
dp.property("node_id", boost::get(&Vertex::node_id, result));
dp.property("label", boost::get(&Edge::box, result));
read_graphviz(ifs, result, dp);
Manipulating the graph
The many algorithms (dijkstra, flow, spanning trees, connected components, etc.) are at your disposal. Or you can mix and match. For example let's filter the nodes that have no connections out:
struct Filter {
Graph const* _g;
bool operator()(Graph::vertex_descriptor v) const {
return boost::size(boost::adjacent_vertices(v, *_g))>0;
}
template <typename T>
bool operator()(T&&) const { return true; /*catch-all*/ }
};
using Filtered = filtered_graph<Graph, Filter, Filter>;
Filter filter { &graph };
Filtered filtered(graph, filter, filter);
Let's write it to graphviz again:
boost::dynamic_properties dp;
dp.property("node_id", boost::get(&Vertex::node_id, filtered));
dp.property("label", boost::get(&Edge::box, filtered));
write_graphviz_dp(std::cout, filtered, dp);
DEMO TIME
The full demo takes your input graph:
And filters it into:
Full Code
Live On Coliru
// http://stackoverflow.com/questions/32279268/using-two-objects-as-hash-key-for-an-unordered-map-or-alternatives
#include <cassert>
#include <iostream>
template <typename T> struct BasicBox {
struct Point { T x, y; };
Point tl, br;
friend std::ostream& operator<<(std::ostream& os, Point const& p) { return os << p.x << ',' << p.y; }
friend std::ostream& operator<<(std::ostream& os, BasicBox const& b) { return os << '[' << b.tl << ';' << b.br << ']'; }
friend std::istream& operator>>(std::istream& is, Point& p) {
char comma;
if (!(is >> p.x >> comma >> p.y) && (comma == ',')) {
is.setstate(std::ios::failbit | is.rdstate());
}
return is;
}
friend std::istream& operator>>(std::istream& is, BasicBox& b) {
char lbrace, semi, rbrace;
if (!(
(is >> lbrace >> b.tl >> semi >> b.br >> rbrace) &&
(lbrace == '[' && semi == ';' && rbrace == ']')
)) {
is.setstate(std::ios::failbit | is.rdstate());
}
return is;
}
};
using Box = BasicBox<int>;
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <libs/graph/src/read_graphviz_new.cpp>
struct Vertex {
std::string node_id;
};
struct Edge {
Box box;
};
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Vertex, Edge>;
#include <fstream>
#include <boost/graph/filtered_graph.hpp>
struct Filter {
Graph const* _g;
bool operator()(Graph::vertex_descriptor v) const {
return boost::size(boost::adjacent_vertices(v, *_g))>0;
}
template <typename T>
bool operator()(T&&) const { return true; /*catch-all*/ }
};
int main() {
using namespace boost;
Graph const graph = []{
std::ifstream ifs("input.txt");
Graph result;
boost::dynamic_properties dp;
dp.property("node_id", boost::get(&Vertex::node_id, result));
dp.property("label", boost::get(&Edge::box, result));
read_graphviz(ifs, result, dp);
return result;
}();
// let's do some random task. Like. You know. Like... Filter out the unconnected nodes
using Filtered = filtered_graph<Graph, Filter, Filter>;
Filter filter { &graph };
Filtered filtered(graph, filter, filter);
boost::dynamic_properties dp;
dp.property("node_id", boost::get(&Vertex::node_id, filtered));
dp.property("label", boost::get(&Edge::box, filtered));
write_graphviz_dp(std::cout, filtered, dp);
}
¹ like e.g. BGL's AdjacencyMatrix
² the format chosen being Graphviz' DOT format: http://www.graphviz.org/
³ Of course you can also use Boost Serialization with BGL models, so you can opt for a more compact binary representation e.g.
You could use a boost::unordered_map with key std::pair<myType*, myType*> in combine with boost::hash. You could declare it as:
boost::unordered_map<std::pair<myType*, myType*>,
std::vector<std::vector<char>>,
boost::hash<std::pair<myType*, myType*>>> dictionary;
Then you could load the characteristics of each pair in the dictionary like in the example below:
dictionary[std::make_pair(&a, &b)] = std::vector<std::vector<char>>(1, {1, 2, 3, 4, 5});
And access them as:
dictionary[std::make_pair(&a, &b)][0][0];
LIVE DEMO
I have a lot of rules that look like this:
cmd_BC = (dlm > timestamp > dlm > cid > dlm > double_)
[
_val = lazy_shared<dc::BoardControl>(_1, _2, _3)
];
I want to make it more readable, like:
cmd_BC = param(timestamp) > param(cid) > param(double_)
or even
cmd_BC = params(timestamp, cid, double_)
As sehe pointed out, it boils down to having some means to automatically expect the delimiters. What are the options here? Myself, I see three possibilities, all flawed:
Use a macro. This wouldn't allow for the shorter variadic form.
Write a custom prefix directive. I don't seem to have enough experience in Spirit's clockwork, but if it's actually not that hard, I will try to.
Write a wrapper function. I tried to no luck the following code:
template <typename T>
auto param(const T & parser) -> decltype(qi::lit(dlm) > parser)
{
return qi::lit(dlm) > parser;
}
but it doesn't compile, failing at
// report invalid argument not found (N is out of bounds)
BOOST_SPIRIT_ASSERT_MSG(
(N < sequence_size::value),
index_is_out_of_bounds, ());
I also tried to return (...).alias(), but it didn't compile too.
This solution is not very "spirit-y" and unfortunately "requires C++11" (I'm not sure how to get the required result type in c++03) but it seems to work. Inspired by the example here.
PS: Oh I didn't see your edit. You have almost the same example.
Update: Added another test using a semantic action with _1,_2 and _3
Update2: Changed the signature of operator() and operator[] following vines's advice in the comments.
Update 3: Added a variadic operator() that constructs a combined parser, and removed operator[] after finding a better solution with boost::proto. Changed slightly the examples.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/proto/proto.hpp>
namespace qi = boost::spirit::qi;
namespace proto = boost::proto;
namespace phx = boost::phoenix;
using namespace boost::proto;
//This is a proto grammar/transform that creates the prefixed parser. The parser created depends on the parser passed (if it's a kleene or not)
// in _make_xxx "_" corresponds to the supplied parser and "_state" to the delimiter
struct CreatePrefixedParser: //you can use _make_greater instead of _make_shift_right if you want to use "expectation"
or_ <
when < dereference<_>, //If it's a kleene parser...
_make_shift_right ( //create the parser -> dlm >> *(parser -dlm)
_state,
_make_dereference (
_make_minus ( _child_c<0> ( _ ),
_state ) ) ) > ,
when < unary_plus<_>, //If it's a +parser
_make_shift_right ( //create the parser -> dlm >> +(parser -dlm)
_state,
_make_unary_plus (
_make_minus ( _child_c<0> ( _ ),
_state ) ) ) > ,
otherwise < //if it's any other parser
_make_shift_right ( //create the parser -> dlm >> (parser -dlm)
_state,
_make_minus ( _,
_state ) ) >
> {};
//-------------------------------------------------------------
//this combines the parsers this way: parser1, parser2, parser3, parser4 -> parser1>>(parser2 >>(parser3 >> parser4)))
//you can use make_expr<tag::greater> if you want to use "expectation"
//I have absolutely no idea when "deep_copy" is required but it seems to work this way
template<typename Delim, typename First, typename ... Rest>
struct myparser
{
static auto combine ( Delim dlm_, const First& first, const Rest&...rest ) ->
decltype ( make_expr<tag::shift_right> ( CreatePrefixedParser() ( deep_copy ( first ), dlm_ ), myparser<Delim, Rest...>::combine ( dlm_, rest... ) ) )
{
return make_expr<tag::shift_right> ( CreatePrefixedParser() ( deep_copy ( first ), dlm_ ), myparser<Delim, Rest...>::combine ( dlm_, rest... ) );
}
};
template<typename Delim, typename Last>
struct myparser<Delim, Last>
{
static auto combine ( Delim dlm_, const Last& last ) -> decltype ( CreatePrefixedParser() ( deep_copy ( last ), dlm_ ) )
{
return CreatePrefixedParser() ( deep_copy ( last ), dlm_ );
}
};
//-----------------------------------------------------------------
template <typename T>
struct prefixer
{
T dlm_;
prefixer ( T dlm ) : dlm_ ( dlm ) {}
template <typename ... Args>
auto operator() ( const Args&... args ) ->
decltype ( deep_copy ( myparser<T, Args...>::combine ( dlm_, args... ) ) )
{
return deep_copy ( myparser<T, Args...>::combine ( dlm_, args... ) );
}
};
template <typename T>
prefixer<T> make_prefixer ( T dlm )
{
return prefixer<T> ( dlm );
}
int main()
{
std::string test = "lameducklamedog";
std::string::const_iterator f ( test.begin() ), l ( test.end() );
auto param = make_prefixer ( qi::lit ( "lame" ) );
qi::rule<std::string::const_iterator> dog = qi::lit ( "do" ) > qi::char_ ( 'g' );
//qi::rule<std::string::const_iterator> duck = qi::lit ( "duck" ) | qi::int_;
qi::rule<std::string::const_iterator,std::string()> quackdog = (param (*qi::alpha) >> param( dog ));
std::string what;
if ( qi::parse ( f, l, quackdog, what ) && f == l )
std::cout << "the duck and the dog are lame, specially the " << what << std::endl;
else
std::cerr << "Uhoh\n" << std::string(f,l) << std::endl;
test = "*-*2.34*-*10*-*0.16*-*12.5";
std::string::const_iterator f2 ( test.begin() ), l2 ( test.end() );
auto param2 = make_prefixer ( qi::lit ( "*-*" ) );
double d;
qi::rule<std::string::const_iterator> myrule = ( param2 ( qi::double_, qi::int_, qi::double_ , qi::double_) ) [phx::ref ( d ) = qi::_1 + qi::_2 + qi::_3 + qi::_4];
if ( qi::parse ( f2, l2, myrule ) && f2 == l2 )
std::cout << "the sum of the numbers is " << d << std::endl;
else
std::cerr << "Uhoh\n";
}
If I understand correctly, you are looking for a way to automatically expect or ignore the delimiter expression (dlm)?
Skippers
This is the classic terrain for Skippers in Spirit. This is especially useful if the delimiter is variable, e.g. whitespace (varying amounts of whitespace acceptable);
bool ok = qi::phrase_parse(
first, last, // input iterators
timestamp > cid > double_, // just specify the expected params
qi::space); // delimiter, e.g. any amount of whitespace
Note the use of phrase_parse to enable grammars with skippers.
Delimited with % parser directive
You could explicitely go and delimit the grammar:
dlm = qi::lit(','); // as an example, delimit by single comma
rule = timestamp > dlm > cid > dlm > double_;
This is tedious. Something that might work about nicely for you (depending on the amount of input validation that should be performed:
dlm = qi::lit(','); // as an example, delimit by single comma
rule = (timestamp | cid | double_) % dlm;
(This would result in a vector of variant<timestampt_t, cid_t, double>)
Roll your own
You could roll your own parser directive, similar to karma::delimit, but for input.
The idea is outlined in this documentation article by Hartmut Kaiser:
Creating Your Own Generator Component for Spirit.Karma
If you're interested, I could see whether I could make this work as an example (I've not used this before). To be honest, I'm surprised something like this doesn't yet exist, and I think it would be a prime candidate for the Spirit Repository
Here's the solution I'm finally satisfied with. It's based on these three answers:
https://stackoverflow.com/a/12679189/396583
https://stackoverflow.com/a/6069950/396583
https://stackoverflow.com/a/3744742/396583
I've decided not to make it accept arbitrary binary functors though, since I doubt it would have any real-world purpose in context of parsing. So,
#include <boost/proto/deep_copy.hpp>
template <typename D>
struct prefixer
{
template<typename... T>
struct TypeOfPrefixedExpr;
template<typename T>
struct TypeOfPrefixedExpr<T>
{
typedef typename boost::proto::result_of::deep_copy
< decltype ( std::declval<D>() > std::declval<T>() ) >::type type;
};
template<typename T, typename... P>
struct TypeOfPrefixedExpr<T, P...>
{
typedef typename boost::proto::result_of::deep_copy
< decltype ( std::declval<D>() > std::declval<T>()
> std::declval<typename TypeOfPrefixedExpr<P...>::type>() ) >::type type;
};
D dlm_;
prefixer ( D && dlm ) : dlm_ ( dlm ) {}
template <typename U>
typename TypeOfPrefixedExpr<U>::type operator() (U && parser )
{
return boost::proto::deep_copy ( dlm_ > parser );
}
template <typename U, typename ... Tail>
typename TypeOfPrefixedExpr<U, Tail...>::type
operator() (U && parser, Tail && ... tail )
{
return boost::proto::deep_copy ( dlm_ > parser > (*this)(tail ...) );
}
};
template <typename D>
prefixer<D> make_prefixer ( D && dlm )
{
return prefixer<D> ( std::forward<D>(dlm) );
}
And it's used like this:
auto params = make_prefixer(qi::lit(dlm));
cmd_ID = params(string) [ _val = lazy_shared<dc::Auth> (_1) ];
cmd_NAV = params(timestamp, double_, double_, double_, double_, double_)
[
_val = lazy_shared<dc::Navigation>( _1, _2, _3, _4, _5, _6 )
];
cmd_BC = params(timestamp, cid, double_)
[
_val = lazy_shared<dc::BoardControl>(_1, _2, _3)
];
Not sure how to explain it - I'm pretty new to C++, but... let me try:
Let's say I have 300+ names (Jeff, Jack...) with 300+ int values (0 or 1). In JS I would use JSON. Something like this:
var people = {"person": [
{"name": "Jeff","val": 0},
{"name": "Jill","val": 1},
{"name": "Jack","val": 0},
{"name": "Jim","val": 1},
{"name": "John","val": 0}
]}
What's the best way to do this in C++?
Thanks.
If you can have duplicate names you can't use a map, so you could use something like this:
struct Person
{
Person( const std::string & n, int v ) : name(n), val(v) {}
std::string name;
int val;
};
int main()
{
std::vector<Person> people;
people.push_back( Person( "Jeff", 0 ) );
people.push_back( Person( "Jill", 1 ) );
...
}
If you wanted uniqueness of names you could do something like this:
std::map<std::string, int> people;
people["Jeff"] = 0;
people["Jill"] = 1;
or
std::map<std::string, Person> people;
people["Jeff"] = Person("Jeff",0);
people["Jill"] = Person("Jill",1);
If you're using this code a lot you can clean up the repeated cruft.
template<typename K, typename V>
struct BuildMap
{
BuildMap() : map_() {}
BuildMap<K,V>& operator()( const K & key, const V & value )
{
map_[key]=value;
return *this;
}
std::map<K,V> operator()() { return map_; }
std::map<K,V> map_;
};
std::map<std::string,int> people = BuildMap<std::string,int>()
( "Jeff", 0 )
( "Jill", 1 )
( "John", 1 )
();
Hope this gives you some ideas.
Take a look at jsoncpp - it is a lightweight json parser, that makes it very easy to use json in your c++ project.
http://sourceforge.net/projects/jsoncpp/
Then you can create a text file, write some entries in the json format there and then open this file in your c++ program. There are plenty of tutorials of how to do it with jsoncpp.
Try looking at std::map.
link here
http://www.cplusplus.com/reference/stl/map/
It's an associative container that is similar to a dictionary. Something like this?
#include <map>
#include <string>
std::map<string,int> person;
void initPeople(){
person["Jeff"] = 0;
person["Jill"] = 1;
person["Jack"] = 0;
person["Jim"] = 1;
person["John"] = 0;
}