I'm using jsoncpp on ubuntu 14.04. Installed with apt-get libjsoncpp-dev libjsoncpp0. I'm not sure what version of jsoncpp it is.
I had a typo in a key name (in C++) and it was really tricky to track down. Consider this example:
#include <iostream>
#include <jsoncpp/json/json.h>
int main(int argc, char** argv) {
Json::Reader reader(Json::Features::strictMode());
Json::Value obj;
std::string json = "{\"mykey\" : 42}";
if (!reader.parse(json.c_str(), obj)) {
std::cout << "JSON parse error" << std::endl;
}
const Json::Value& mykey1 = obj["mykey"];
std::cout << "mykey1:" << mykey1.asInt() << std::endl;
const Json::Value& mykey2 = obj["mykey_typo"];
std::cout << "mykey2:" << mykey2.asInt() << std::endl;
return 0;
}
I'm getting this output:
mykey1:42
mykey2:0
The fact that an access to a non-existent key produces a value of 0 is scary to me. It means if there is a typo the program will just use zero instead of the correct value. I'd rather know there was a typo. Is there a way to make jsoncpp throw or what is the recommended approach here?
I can write a helper that calls isMember and then does the lookup, but was wondering if the library itself has a solution.
It looks like by design the behavior of jsoncpp asInt returns 0 when the value is null. Perhaps do a null check before using the value?
Take a look at line 721 in the source.
Related
Today I did a lot of research online about how to create a directory on C++
and found a lot of way to do that, some easier than others.
I tried the _mkdir function using _mkdir("C:/Users/..."); to create a folder. Note that the argument of function will be converted into a const char*.
So far, so good, but when I want to change the path, it does not work (see the code below). I have a default string path "E:/test/new", and I want to create 10 sub-folders: new1, new2, newN, ..., new10.
To do that, I concatenate the string with a number (the counter of the for-loop), converted into char using static_cast, then I transform the string using c_str(), and assign it to a const char* variable.
The compiler has no problem compiling it, but it doesn't work. It prints 10 times "Impossible create folder n". What's wrong?
I probably made a mistake when transforming the string using c_str() to a get a const char*?.
Also, is there a way to create a folder using something else? I looked at CreateDirectory(); (API) but it uses keyword like DWORD HANDLE, etc., that are a little bit difficult to understand for a no-advanced level (I don't know what these mean).
#include <iostream>
#include <Windows.h>
#include<direct.h>
using namespace std;
int main()
{
int stat;
string path_s = "E:/test/new";
for (int i = 1; i <= 10; i++)
{
const char* path_c = (path_s + static_cast<char>(i + '0')).c_str();
stat = _mkdir(path_c);
if (!stat)
cout << "Folder created " << i << endl;
else
cout << "Impossible create folder " << i << endl;
Sleep(10);
}
return 0;
}
If your compiler supports c++17, you can use filesystem library to do what you want.
#include <filesystem>
#include <string>
#include <iostream>
namespace fs = std::filesystem;
int main(){
const std::string path = "E:/test/new";
for(int i = 1; i <= 10; ++i){
try{
if(fs::create_directory(path + std::to_string(i)))
std::cout << "Created a directory\n";
else
std::cerr << "Failed to create a directory\n";\
}catch(const std::exception& e){
std::cerr << e.what() << '\n';
}
}
return 0;
}
The problem is that (path_s + static_cast<char>(i + '0')) creates a temporary object. One whose life-time ends (and is destructed) just after c_str() has been called.
That leaves you with a pointer to a string that no longer exist, and using it in almost any way will lead to undefined behavior.
Instead save the std::string object, and call c_str() just when needed:
std::string path = path_s + std::to_string(i);
_mkdir(path.c_str());
Note that under Linux, you can use the mkdir command as follows:
#include <sys/stat.h>
...
const int dir_err = mkdir("foo", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (-1 == dir_err){
printf("Error creating directory!n");
exit(1);
}
More information on it can be gleaned from reading man 2 mkdir.
I use Crypto++ 5.6.3 and iI need the FileSource Skip(...) function. Unfortunately this function does nothing!
Here is a example for this function.
string filename = ...;
string str;
FileSource file(filename, false, new HexEncoder(new StringSink(str)));
file.Skip(24);
file.PumpAll();
Can somebody help me?
I use Crypto++ 5.6.3 and iI need the FileSource "skip(...) function. Unfortunately this function does nothing!
I was able to duplicate this using strings under Master, 5.6.3, and 5.6.2 on OS X 10.8.5 and Ubuntu 14.04.
$ cat test.cxx
#include <string>
#include <iostream>
using namespace std;
#include <filters.h>
#include <hex.h>
using namespace CryptoPP;
int main(int argc, char* argv[])
{
string str1, str2;
HexEncoder enc(new StringSink(str1));
for(unsigned int i=0; i < 32; i++)
enc.Put((byte)i);
enc.MessageEnd();
cout << "str1: " << str1 <<endl;
StringSource ss(str1, false, new StringSink(str2));
ss.Skip(10);
ss.PumpAll();
cout << "str2: " << str2 << endl;
return 0;
}
And:
$ ./test.exe
str1: 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F
str2: 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F
Crypto++ 5.6.2 is significant because it was the last version Wei worked on before turning the library over to the community. An issue in 5.6.2 is just a latent bug and we encounter them on occasion, just like any other project. ("Wei" bugs are actually kind of rare, and they are closer to "Knuth" bugs in his Art of Computer Programming).
If its a 5.6.3 and above problem, then it means the community broke it. If the community broke it, then we need to perform a post-mortem and analyze how/why we managed to break something that used to work.
Here's the bug report for the library: Issue 248: Skip'ing on a Source does not work. We are trying to determine if its a bug; and if so, then how to proceed.
EDIT 1: I was able to investigate the issue a little more. You can read the analysis at Comment 242890863. The short of it is, Skip is used to discard bytes on an output buffer (an AttachedTransformation()), so things are somewhat working as expected. However, there's nothing intuitive about Skip not working on the Source, and only working on the attached Filter (q.v., we're here).
I also asked for some feedback on the mailing list at Issue 248: Skip'ing on a Source does not work. DB and WD spotted it right away - its a design issue in the library.
Here's the workaround you can use for the moment. Effectively, you Pump() into a null Filter which discards the input as expected. Then you attach the real filter chain to handle the real processing.
#include <string>
#include <iostream>
using namespace std;
#include <filters.h>
#include <hex.h>
using namespace CryptoPP;
int main(int argc, char* argv[])
{
string str1, str2;
HexEncoder enc(new StringSink(str1));
for(unsigned int i=0; i < 32; i++)
enc.Put((byte)i);
enc.MessageEnd();
cout << "str1: " << str1 <<endl;
// 'ss' has a NULL AttachedTransformation()
StringSource ss(str1, false);
ss.Pump(10);
// Attach the real filter chain to 'ss'
ss.Attach(new StringSink(str2));
ss.PumpAll();
cout << "str2: " << str2 << endl;
return 0;
}
It produces the expected output:
$ ./test.exe
str1: 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F
str2: 05060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F
In your sample program, I believe the workaround would be:
FileSource file(filename, false);
file.Pump(24);
file.Attach(new HexEncoder(new StringSink(str)));
file.PumpAll();
EDIT 2: Here's a slightly more verbose way to achieve the work around (thanks DB). It stresses the point that bytes are being discarded. TheBitBucket() is simply a discard filter, and it serves the same purpose as a null AttachedTransformation().
int main(int argc, char* argv[])
{
string str1, str2;
HexEncoder enc(new StringSink(str1));
for(unsigned int i=0; i < 32; i++)
enc.Put((byte)i);
enc.MessageEnd();
cout << "str1: " << str1 <<endl;
StringSource ss(str1, false, new Redirector(TheBitBucket()));
ss.Pump(10);
ss.Detach(new StringSink(str2));
ss.PumpAll();
cout << "str2: " << str2 << endl;
return 0;
}
There's another subtle difference in the program above: It calls Detach, which free's the former filter chain. If you called Attach, then the former chain would be detached, returned to the caller but not free'd.
When using boost_filesystem, Boost keeps adding quotation marks to the filenames.
foo.cpp:
#include <iostream>
#include <boost/filesystem.hpp>
int main( int argc, char * argv[] )
{
std::cout << argv[0] << std::endl;
boost::filesystem::path p( argv[0] );
std::cout << p << std::endl;
std::cout << p.filename() << std::endl;
return 0;
}
Compiled:
g++ foo.cpp -o foo -lboost_filesystem -lboost_system
Output:
./foo
"./foo"
"foo"
This is somewhat unexpected, and inconvenient in my case. Is this really intentional, or is my somewhat older version of Boost (1.46.1) buggy in this respect? Is there some way I could avoid them being added?
I perused the documentation, but aside from the tutorials not showing those quotation marks in their example output, I was not enlightened.
This is actually a bug filed on the Boost framework on version 1.47.0.
The proposed workaround is:
std::cout << path("/foo/bar.txt").filename().string()
It's intentional because unexpected embedded spaces and confuse related code. The best you can do is probably:
boost::replace_all(yourquotedstring, "\"", "");
EDIT
Although, according to this link, you can try something like:
std::cout << path("/foo/bar.txt").filename().string();
I am using Jsoncpp to parse json-formats for c++.
I do not understand how it works though; there is a lack of documentation and examples to get me started, and I was wondering if anyone could give me some quick pointers. The only examples I've found deals with files...
I'm using a HTTP stack to get a json-message in a buffer. For example, a buffer contains the message {"state":"Running"}. How do I use the Json::reader to parse this? Again the only example I've found deals with reading from files
How do you write values to a Json-message? For example I want to write "monkey : no" and "running : yes" to a Json-message which I can then use in my GET request.
Thanks
UPDATE:
on 1), for example, how to parse a buffer containing a json-message like this:
char* buff;
uint32_t buff_size;
Maybe this is good sample for first part of your question:
Json::Value values;
Json::Reader reader;
reader.parse(input, values);
Json::Value s = values.get("state","default value");
There is anything but lack of documentation. Yes, it's mainly reference documentation, but it's quite good and well cross-linked.
Just read the documentation
Just use this class or possibly use the other class
Sample code for your reference, below:
file.json
{
"B":"b_val2",
"A":{
"AA":"aa_val1",
"AAA" : "aaa_val2",
"AAAA" : "aaaa_val3"
},
"C":"c_val3",
"D":"d_val4"
}
jsoncpp usage scenario as below, for above sample json file.
#include <iostream>
#include "json/json.h"
#include <fstream>
using namespace std;
int main(){
Json::Value root;
Json::Reader reader;
const Json::Value defValue; //used for default reference
std::ifstream ifile("file.json");
bool isJsonOK = ( ifile != NULL && reader.parse(ifile, root) );
if(isJsonOK){
const Json::Value s = root.get("A",defValue);
if(s.isObject()){
Json::Value s2 = s.get("AAA","");
cout << "s2 : " << s2.asString() << endl;
}else{
cout << "value for key \"A\" is not object type !" << endl;
}
}
else
cout << "json not OK !!" << endl;
return 1;
}
Output::
s2 : aaa_val2
Additionally, I have used the "amalgamate.py" for generating and using the jsoncpp for the sample source above.
Update 2: I'm not sure why this is still being upvoted (March 2014). This appears to be fixed since I asked this question many years ago. Make sure you're using a recent version of boost.
UPDATE: Perhaps C++ streams need to be initialized in order to format numbers, and the initialization is not happening when the shared library is loaded in Python?
I am calling
cout << 1 << "!" << endl;
in a method that is exported to a shared library via boost.python. It doesn't print anything, but if I do
cout << "%" << "!" << endl;
it works.
This is important because I want to do this:
ostream& operator <<(ostream &os, const Bernoulli& b) {
ostringstream oss;
oss << b.p() * 100.0 << "%";
return os << oss.str();
}
I exposed that by doing this:
BOOST_PYTHON_MODULE(libdistributions)
{
class_<Bernoulli>("Bernoulli")
.def(init<>())
.def(init<double>())
.def("p", &Bernoulli::p)
.def("set_p", &Bernoulli::set_p)
.def("not_p", &Bernoulli::not_p)
.def("Entropy", &Bernoulli::Entropy)
.def("KL", &Bernoulli::KL)
.def(self_ns::str(self))
;
}
but when I call the str method in python on a Bernoulli object, I get nothing. I suspect the simpler cout problem is related.
I also run into this problem a while ago, using self_ns as outlined in the answers here Build problems when adding `__str__` method to Boost Python C++ class
The reason for using self_ns is explained by Dave himself here http://mail.python.org/pipermail/cplusplus-sig/2004-February/006496.html
Just for the sake of debugging, please try
inline std::string toString(const Bernoulli& b)
{
std::ostringstream s;
s << b;
return s.str();
}
And replace .def(self_ns::str(self)) with
class_<Bernoulli>("Bernoulli")
[...]
.def("__str__", &toString)
Have you tried using boost::format instead? Since you are already using boost, it shouldn't be too much of a hassle.
boost::format( "%d%%" ) % ( b.p() * 100.0 )
Another thing, try passing std::endl explicitly to the output os.
have you tried flushing the stream prior to using the .str() method?
oss << b.p() * 100.0 << "%" << std::flush;