I want to share structured data between C++ and Python languages using MessagePack like this one:
{
"t" : [ [t00,...,t0N], ... , [tM0,...,tMN] ],
"x" : [ x0,..,xN],
"P" : [ [P00, ..., P0N], ..., [PM0,...,PMN] ]
}
The number of variables is optional so in some cases I will have for example only:
{
"t" : [ [t00,...,t0N], ... , [tM0,...,tMN] ]
}
Decoding this in Python is pretty simple, my problem is to figure out
how to decode this in C++ if I don't know in advance the structure of
the data ? or the exact number of variables that I would have; is it
possible to iterate the structure in these cases?
I managed to handle a "fixed" data structure ( always with the same
number of variables ) defining a struct for example:
struct variables
{
std::vector< std::vector<double> > t;
std::vector< double > x;
std::vector< std::vector<double> > P;
MSPACK_DEFINE_MAP( t, x, P );
};
std::stringstream inBuffer;
.... (read data )
std::string str( inBuffer.str() );
msgpack::object_handle oh = msgpack::unpack( str.data(), str.size() );
msgpack::object deserialized = oh.get();
variables var;
deserialized.convert( var );
Is there a better way to accomplish this ?, how could manage optional
variables that could not appear in the structure ?; I repeat the
previous question: could I iterate an unknown data structure in C++?,
how ?
Thanks in advance!
Regards, Ernesto
There are two ways to treat unknown data structure.
The first way is using parse/visitor mechanism.
Here is an example:
#include <msgpack.hpp>
#include <sstream>
#include <iostream>
// This is a simple print example visitor.
// You can do any processing in your visitor.
struct my_visitor : msgpack::null_visitor {
bool start_map_key() {
processing_map_key = true;
return true;
}
bool end_map_key() {
processing_map_key = false;
return true;
}
bool start_array(uint32_t size) {
std::cout << "array (size:" << size << ")[" << std::endl;
return true;
}
bool end_array() {
std::cout << "]" << std::endl;
return true;
}
bool visit_str(const char* v, uint32_t size) {
if (processing_map_key) {
std::cout << "map key:" << std::string(v, size) << std::endl;
}
return true;
}
bool visit_positive_integer(uint64_t v) {
std::cout << "found value:" << v << std::endl;
return true;
}
bool processing_map_key = false;
std::string indent;
};
int main() {
// create test data
std::stringstream ss;
msgpack::packer<std::stringstream> pk(ss);
pk.pack_map(1);
pk.pack("t");
pk.pack_array(2);
pk.pack_array(3);
pk.pack(1);
pk.pack(2);
pk.pack(3);
pk.pack_array(3);
pk.pack(4);
pk.pack(5);
pk.pack(6);
// print data (for debug)
{
auto oh = msgpack::unpack(ss.str().data(), ss.str().size());
std::cout << oh.get() << std::endl;
}
// apply visitor
{
my_visitor mv;
msgpack::parse(ss.str().data(), ss.str().size(), mv);
}
}
Running demo: https://wandbox.org/permlink/3NrR4IMDIuLTk9e9
See https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_visitor.
The other way is using msgpack::type::variant or `msgpack::type::variant_ref.
The former copies data, you can update it. The latter doesn't copy data. You cannot update it.
This approach requires boost. So you need to define MSGPACK_USE_BOOST. I recommend defining as a compiler option.
// Boost is required
#define MSGPACK_USE_BOOST
#include <msgpack.hpp>
#include <sstream>
#include <iostream>
struct my_visitor:boost::static_visitor<void> {
void operator()(uint64_t v) const {
std::cout << "positive insteger:" << v << std::endl;
}
// const is required for map key because std::multimap's key (first) is const.
void operator()(std::string const& v) const {
std::cout << "string:" << v << std::endl;
}
void operator()(std::vector<msgpack::type::variant>& v) const {
std::cout << "array found" << std::endl;
for (auto& e : v) {
boost::apply_visitor(*this, e);
}
}
void operator()(std::multimap<msgpack::type::variant, msgpack::type::variant>& v) const {
std::cout << "map found" << std::endl;
for (auto& e : v) {
std::cout << "key:" << std::endl;
boost::apply_visitor(*this, e.first);
std::cout << "value:" << std::endl;
boost::apply_visitor(*this, e.second);
}
}
template <typename T>
void operator()(T const&) const {
std::cout << " match others" << std::endl;
}
};
int main() {
// create test data
std::stringstream ss;
msgpack::packer<std::stringstream> pk(ss);
pk.pack_map(1);
pk.pack("t");
pk.pack_array(2);
pk.pack_array(3);
pk.pack(1);
pk.pack(2);
pk.pack(3);
pk.pack_array(3);
pk.pack(4);
pk.pack(5);
pk.pack(6);
auto oh = msgpack::unpack(ss.str().data(), ss.str().size());
std::cout << oh.get() << std::endl;
msgpack::type::variant v = oh.get().as<msgpack::type::variant>();
boost::apply_visitor(my_visitor(), v);
}
Running demo: https://wandbox.org/permlink/HQwJjfwW8rLEMi0d
See https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_variant
Here are exampless:
https://github.com/msgpack/msgpack-c/blob/master/example/boost/msgpack_variant_capitalize.cpp
https://github.com/msgpack/msgpack-c/blob/master/example/boost/msgpack_variant_mapbased.cpp
Both ways can treat unpredictable data structure. You need to do some visitor processing. If the data structure is predictable some extent, your original approach is also good way.
Actually there is a simpler way, if you are dealing with maps (like stated in the question), not arrays.
msgpack::object_handle oh = msgpack::unpack(/* some data */);
std::map<std::string,msgpack::type::variant> map = obj.convert();
This way you will get a map with all the data, no need for a visitor or boost.
Related
my code was
#include <iostream>
#include <list>
#include <functional>
using namespace std;
void tes(std::string s)
{
cout << "tes " << s << '\n';
}
void tes2(std::string s)
{
cout << "tes " << s << '\n';
}
void tes3(std::string s)
{
cout << "tes " << s << '\n';
}
int main()
{
using FuncType = std::function<void(std::string&)>;
std::list<std::pair<int, FuncType>> events =
{
{1, std::bind(tes, "1")},
{2, std::bind(tes2, "2")},
{3, std::bind(tes3, "3")} };
int t = 1;
auto remove_func = [&t](std::pair<int, FuncType> pair)
-> bool
{
return pair.first == t;
};
events.remove_if(remove_func);
for (auto ev : events)
{
std::cout << ev.first << ' ' ;
ev.second;
}
}
the result just display ev.first, but not the ev.second. what happened??
how to resolve this problem?? i mean display the string in FuncType function
such "2" and "3". or fixed this code properly to work to display each.
There are a couple of issues in the posted code
// ...
void tes(std::string s)
{ // ^^^^^^^^^^^^^
}
// ...
using FuncType = std::function<void(std::string&)>;
// ^^^^^^^^^^^^
std::list<std::pair<int, FuncType>> events =
{
{1, std::bind(tes, "1")},
{2, std::bind(tes2, "2")},
{3, std::bind(tes3, "3")}
// ^^^^^^^^^^^^^^^^^^^^
};
The functions like tes have one parameter of type std::string, the alias FuncType is a std::function with one parameter of type std::string&, but the pairs in events are assigned the results of std::bind, which have zero parameters (they are, well, binded).
Moreover, in the printing loop
for (auto ev : events)
{
std::cout << ev.first << ' ' ;
ev.second; // <- This does nothing, it should be a call.
}
The fix1 is straightforward:
using FuncType = std::function<void(void)>;
// ^^^^^^
// ...
for (auto ev : events)
{
std::cout << ev.first << ' ' ;
ev.second();
// ^^
}
1) https://godbolt.org/z/5f8srj9va
Fix the misprint:
std::cout << ev.first << ' ' << ev.second;
I switched from c to c++ recently and just can't figure out what I'm doing wrong here.
I would like to access and set the member of a map via another function.
Here is my example which you can just copy to cpp.sh or so if you like
#include <iostream>
#include <map>
using namespace std;
struct test{
int i;
int j;
};
void addValues(test* val){
if (val == NULL){
val = new test();
cout<<"new";
}
val->i = 10;
val->j = 12;
}
void printVal(test* val){
cout<<"finish " << val->i << " " << val->j;
}
int main()
{
map<string, test*> bla = {{"test1",NULL}};
addValues(bla.at("test1"));
printVal(bla.at("test1"));
return 0;
}
code from my project is a little bit more complex but it's basically this problem. I created a test in addValues() and have not deleted it. Why am I not able to print this value in printVal()? What am I missing?
Thanks in advance!
Parameters are passed by value. Pointers are no exception to that. Your addValues modifies a local copy of the pointer when a nullptr is passed. Modifying that local copy does not affect the pointer in the map. Pass the pointer by reference:
void addValues(test*& val){
if (val == nullptr){
val = new test();
cout<<"new";
}
val->i = 10;
val->j = 12;
}
Or better yet, do not use raw pointers in the first place. Moreover, consider to write a constructor that initializes the members of test instead of relying on the caller to initialize them.
Example :
#include <iostream>
#include <map>
//using namespace std; NO teach yourself not to do this.
struct test
{
int i = 0; // <== in c++ you can initialize values of structs
int j = 0;
};
// this instead of printVal
std::ostream& operator<<(std::ostream& os, const test& t)
{
os << "i = " << t.i << ", j = " << t.j << "\n";
return os;
}
int main()
{
std::map<std::string, test> map =
{
{"test1",{1,1}},
{"test2",{2,2}},
};
// loop over all entries in the map
// range based for loop.
// each entry in the map is a key,value pair (not they key, not the value but a pair)
// https://en.cppreference.com/w/cpp/language/range-for
std::cout << "range based for over keyvalue pairs\n";
for (const auto& kv : map)
{
// note kv.second is where we use operator<< from earlier.
std::cout << "Key : " << kv.first << ", value : " << kv.second << "\n";
}
std::cout << "\n";
// structured bindings make code more readable
// https://en.cppreference.com/w/cpp/language/structured_binding
std::cout << "range based for using structured bindings \n";
for (const auto& [key, value] : map)
{
std::cout << "Key : " << key << ", value : " << value <<"\n";
}
std::cout << "\n";
return 0;
}
In C++, I want to use a map of functions with different type of input or output.
Do to so, I found that using a map with any type could be a way.
But I get several problems. First, I can not use directly the functions in the map.
However, I can use a lambda function to wrap the functions then use these lambda functions in the map.
But, I get a second problem, I still need to cast with the lambda function which is not a variable. This makes a use from a string variable complicated.
Here is a MWE:
#include <any>
#include <functional>
#include <iostream>
#include <map>
#include <string>
void funct0()
{
std::cout << "funct0" << std::endl;
}
void funct1(int p)
{
std::cout << "funct1 " << p << std::endl;
};
int funct2(int p, std::string s)
{
std::cout << "funct2 " << s << std::endl;
return p+1;
};
float funct3(int a, float b)
{
std::cout << "funct3 " << std::endl;
return a +b;
}
auto funct4(int a, float b)
{
std::cout << "funct4 " << std::endl;
std::vector<float> v;
v.push_back(a);
v.push_back(b);
return v;
}
int main()
{
std::map<std::string, std::any> mapFunct;
mapFunct["F0"]= funct0;
// mapFunct["FO"](); // error: no match for call to ‘(std::map<std::__cxx11::basic_string<char>, std::any>::mapped_type {aka std::any}) ()’
mapFunct["F1"]= funct1;
// mapFunct["F1"](12); // error: no match for call to ‘(std::map<std::__cxx11::basic_string<char>, std::any>::mapped_type {aka std::any}) (int)’
// WHY THIS IS NOT WORKING ?
// From this link: https://stackoverflow.com/questions/61969316/is-it-possible-to-put-lambda-expressions-into-a-map-or-list-in-c
auto lambda0 = [](){funct0();};
auto lambda1 = [](int p) { funct1(p); return p; };
auto lambda2 = [](int p, std::string s) { return funct2(p,s); };
auto lambda3 = [](int a, float b){return funct3(a,b);};
auto lambda4 = [](int a, float b){return funct4(a,b);};
std::map<std::string, std::any> mapLambda;
mapLambda["L0"]=lambda0;
mapLambda["L1"]=lambda1;
mapLambda["L2"]=lambda2;
mapLambda["L3"]=lambda3;
mapLambda["L4"]=lambda4;
std::any_cast<decltype(lambda0)>(mapLambda["L0"])();
std::any_cast<decltype(lambda1)>(mapLambda["L1"])(2);
std::cout << std::any_cast<decltype(lambda2)>(mapLambda["L2"])(4, "HELLO") << std::endl;
std::cout << std::any_cast<decltype(lambda3)>(mapLambda["L3"])(3, 4.32) << std::endl ;
auto vec4= std::any_cast<decltype(lambda4)>(mapLambda["L4"])(6, 9.1);
std::cout << "vec4" << vec4[1] << vec4[2] << std::endl ;
std::vector<std::string> inputString;
inputString.push_back("L3(3, 4.32)");
inputString.push_back("L4(6, 9.1)");
// Using a for loop with iterator
for(auto it = std::begin(inputString); it != std::end(inputString); ++it) {
std::cout << *it << "\n";
std::string line=*it;
std::string functionInput = line.substr( 0, line.find("(") );
std::cout << functionInput << std::endl;
// argumentsInput= ;
mapLambda[functionInput](argumentsInput);
}
};
So my question are:
Why my example is working with lambda functions and not the functions ?
How can I make the last part of my example works only from the inputString variable? (ie, knowing the correct casting from the string variable)
What you probably want is something like this:
using CallWrapper = std::function<void(const std::string&)>;
std::map<std::string, CallWrapper> mapLambda;
mapLambda["L0"] = [funct0](const std::string&) { funct0(); };
mapLambda["L1"] = [funct1](const std::string& args) {
int p = ...; // parse the argument from `args`
funct1(p);
};
mapLambda["L2"] = [funct2](const std::string& args) {
// parse the arguments from `args`
int p = ...;
std::string s = ...;
funct2(p, s);
};
Now you can run the loop you envision:
for(const std::string& line : inputString) {
size_t pos = line.find('(');
std::string functionInput = line.substr( 0, pos);
std::string argumentsInput = line.substr(pos);
mapLambda[functionInput](argumentsInput);
}
The hard part, of course, is "parse the arguments from args", left as an exercise for the reader.
std::any_cast needs to cast to constructible types. A standard C++ function is neither a type nor constructible (it's just a group of statements given a name [edit: this isn't technically true, but what's going on under the hood is fairly complicated]), but std::function is. One way to get around this is to assign a standard C++ function to an std::function. Here's an example using a std::map like you were using:
#include <any>
#include <functional>
#include <iostream>
#include <map>
int my_func(int val) { return val + 1; }
std::function<int(int)> f = my_func;
int main() {
auto my_map = std::map<std::string, std::any>();
my_map["func"] = f;
std::cout << std::any_cast<std::function<int(int)>>(my_map["func"])(13) << std::endl; // prints "14"
return 0;
}
Lambdas are constructible types, which is why your code works for lambdas.
To answer your second question: I don't think it's possible. Functions with different signatures are different types, and you have to know what you're casting to. std::function<int(int, string)> and std::function<float(int, float)>, for example, are different types.
Also, the intended purpose of lambdas is to be used once then discarded. If you're going to keep lambdas around for reuse, it's better to simply just use functions.
The system is able to figure out the type of each element using a.type().name() but seriously is not able to print them?
#include <iostream>
#include <vector>
#include <any>
#include <string>
#include <algorithm>
template<typename Last>
void addElement(std::vector<std::any>& container, Last last) {
std::cout << "Last = " << last << std::endl;
container.push_back(last);
}
template<typename First, typename... Rest>
void addElement(std::vector<std::any>& container, First first, Rest... rest) {
std::cout << "Elem = " << first << std::endl;
container.push_back(first);
addElement(container, rest...);
}
template<typename... Ts>
std::vector<std::any> createAnyVector(Ts... ts) {
std::vector<std::any> container;
addElement(container, ts...);
return container;
}
int main() {
std::cout << "ANYVECTOR" << std::endl;
std::vector<std::any> container = createAnyVector("Hello", 3.14, 'A', true, 42);
std::cout << "Number of elements in container = " << container.size() << std::endl; // 5 correct.
for (const auto& a : container) {
std::cout << a.type().name() << ", " << "HERE?" << std::endl;
}
}
If I just write a at the place where now HERE? stands, it returns the error:
No operator << matches these operands
operand types are: std::basic_ostream<char, std::char_traits<char>> << const << std::any
As the comments point out, you cannot do anything directly with std::any, you can just hold them.
If you control the API and have the option to create a wrapper around std::any, you can maintain a function pointer to print the current std::any's contents like so:
struct printable_any {
template <class T>
printable_any(T&& t) : m_val(std::forward<T>(t)) {
m_print_fn = [](std::ostream& os, const std::any& val) {
os << std::any_cast<std::decay_t<T>>(val);
};
}
private:
using print_fn_t = void(*)(std::ostream&, const std::any&);
std::any m_val;
print_fn_t m_print_fn;
friend std::ostream& operator<<(std::ostream& os, const printable_any& val) {
val.m_print_fn(os, val.m_val);
return os;
}
};
Note that even with this, you cannot take a random std::any and print it, you have to construct it yourself along with the function pointer.
Usage:
int main() {
std::vector<printable_any> vals {
"Hello", 3.14, 'A', true, 42
};
for (auto& v : vals) {
std::cout << v << '\n';
}
}
Prints
Hello
3.14
A
1
42
https://godbolt.org/z/o1sejrv1e
Well, what would this print:
struct Foo {
void *p;
};
any v = Foo{};
cout << v << '\n'; // what should this print?
There is no toString equivalent in C++. If you want that behavior, you may create your own hierarchy:
struct Printable {
virtual void print(ostream&) const = 0;
};
auto& operator<<(ostream& os, any const& obj) {
any_cast<Printable const&>(obj).print(os);
return os;
}
Or customize it as you wish and handle the error cases as you like, also add special cases for the integer types if you want, etc.
Does the vector have to be of type any or are you allowed to at least specify the kind of types that are stored in the vector? Because if you can at least specify which types are allowed, I recommend using std::variant.
example: std::vector<variant<int, string>> myVectorVariant;
After that, you can actually print to the console the items in your vector through the use of std::get_if from std::variant. copy-able function below to output your vector for the most of the main primitive types: int, float, char, string.
#include <variant>
#include <vector>
#include <iostream>
using namespace std;
void printVectorVariantValues(vector<variant<int, float, char, string>> arg) {
try {
for (auto val : arg) {
if (const auto intPtr (get_if<int>(&val)); intPtr)
cout << *intPtr << " ";
else if (const auto floatPtr (get_if<float>(&val)); floatPtr)
cout << *floatPtr << " ";
else if (const auto charPtr (get_if<char>(&val)); charPtr)
cout << *charPtr << " ";
else if (const auto stringPtr (get_if<string>(&val)); stringPtr)
cout << *stringPtr << " ";
}
} catch (bad_variant_access err) {
cout << "something" << " ";
}
cout << "DONE" << endl;
}
int main() {
vector<variant<int, float, char, string>> myVectorVariant = {1, 2, 'a', 'b', 3, 0.4f, 0.5f, "c", "DeF", 0.6f, "gHi", "JkL" };
printVectorVariantValues(myVectorVariant);
return 0;
}
/*
output below:
1 2 a b 3 0.4 0.5 c DeF 0.6 gHi JkL DONE
*/
I'm learning C++ so my question might seem a bit stupid. I wanted to build a function that print every element in a vector. I come up with that but it seems to display the address of every element. I google it and find a nice solution but i wanted to do it this way so if anyone can explain me where i'm doing something wrong.
My code :
void display_vector(std::vector<int>& to_display);
int main()
{
std::vector<int> vector_to_sort = { 2,6,7,2,1,80,2,59,8,9 };
display_vector(vector_to_sort);
}
void display_vector(std::vector<int> &to_display)
{
for (int i = 0; i < to_display.size(); i++)
{
std::cout << to_display[i] << ', ';
}
std::cout << '\n';
}
The solution i found on internet :
#include <iterator>
void display_vector(const vector<int> &v)
{
std::copy(v.begin(), v.end(),
std::ostream_iterator<int>(std::cout, " "));
}
Output of my code :
21129661129671129621129611129680112962112965911296811296911296
You use ", " instead of ', '.
You can use any of the following print mechanism in the display() function:
void display_vector(std::vector<int> &to_display)
{
//by using Normal for loop
for (auto i = to_display.begin(); i != to_display.end(); ++i) {
cout << *i << " ";
}
cout << endl;
//by using Range based for loop
for (int & i : to_display) {
cout << i << " ";
}
std::cout << '\n';
}
In this statement
std::cout << to_display[i] << ', ';
^^^^^^
you are using a multybyte character literal that has an implementation defined value.
Substitute it for string literal ", ".
As for the function then for starters if the vector is not being changed in the function then the parameter should be a constant reference.
You can use the range-based for loop to outfput elements of the vector like for example
#include <iostream>
#include <vector>
std::ostream & display_vector( const std::vector<int> &to_display, std::ostream &os = std::cout );
int main()
{
std::vector<int> vector_to_sort = { 2,6,7,2,1,80,2,59,8,9 };
display_vector(vector_to_sort) << '\n';
}
std::ostream & display_vector( const std::vector<int> &to_display, std::ostream &os )
{
for ( const auto &item : to_display )
{
os << item << ", ";
}
return os;
}
Using such a function you can for example output the vector in a text file.
Just replace below line
std::cout << to_display[i] << ', ';
with
std::cout << to_display[i] << ", ";
Also note that if you just want to display vector in function then declare parameter as const reference as shown below
void display_vector(const std::vector<int> &to_display);
The debuggers make it easy to examine vectors but I include a simple template to print out vectors of standard types and often use it when debugging data that I wish to look at with other tools.
template<class T>
void print(const std::vector<T>& v){
for (auto x: v)
std::cout << x << std::endl;
}