How do i read/write JSON with c++? - c++

I would like to know how to read/write a JSON file using C++.
I will be using this file to store player info & setting for a simple game I'm making.
It's nothing fancy, just a console number guessing game, but I just use it to learn stuff.
I have to know how to read & write specific parts of a JSON.

Using a library, it can be done quite easily:
#include <nlohmann/json.hpp>
#include <iostream>
int main() {
// read file
auto json = nlohmann::json::parse("{\"value1\": \"string\"}");
// mutate the json
json["value1"] = "new string";
// write to a stream, or the same file
std::cout << json; // print the json
}
C++ don't have the built-ins for dealing with json. You can implement your own json data structure, or use one available like nlohmann/json or simdjson
You could create your own parser using pure C++ with the standard library only, but I would advise against.

Using struct_mapping it can be done:
#include "struct_mapping/struct_mapping.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
struct Planet
{
std::string name;
double mass;
bool populated;
};
int main()
{
struct_mapping::reg(&Planet::name, "name");
struct_mapping::reg(&Planet::mass, "mass");
struct_mapping::reg(&Planet::populated, "populated");
Planet planet;
auto stream = std::ifstream("planet.json");
struct_mapping::map_json_to_struct(planet, stream);
planet.name = "Mars";
planet.populated = false;
std::ostringstream out_json_data;
struct_mapping::map_struct_to_json(planet, out_json_data, " ");
std::cout << out_json_data.str() << std::endl;
}
Data file example
{
"name": "Earth",
"mass": 1234,
"populated": true
}

I wrapped boost property tree initialized around classes and macros and it's close to type reflection(but it's still missing a reflection library to finish it off).
It Also supports nesting of types something that alot of so called "fantastic json" libraries fall short of when you get into
the nitty gritty.
So say you have a class what you want to serialize or deserialize in JSON:
I'd write in my cpp
class MyClass: public virtual Algorithm::Interface::ISimpleSerializedType
{
public:
int a;
string b;
// could be simplified further via a variadic macro to generate //SimplePropertyTree
virtual Algorithm::Interface::IPropertyTree SimplePropertyTree(Algorithm::Interface::IPropertyTree& pt, bool toPropertyTree)
{
PSER(a, int)
PSER(b, string)
}
};
The JSON would look something like
{
a : "1"
b :"somestring"
}
My read and write unit tests/snippets would look like this:
//write
MyClass entity;
entity.a = 1;
entity.filename = "test.json";
entity.ToFile();
// read
MyClass entity;
entity.filename = "test.json";
entity.FromFile(); // everything is loaded
code for Algorithm::Interface::ISimpleSerializedType
#ifndef I_SIMPLE_SERIALIZED_TYPE_H
#define I_SIMPLE_SERIALIZED_TYPE_H
#include "IType.h"
#include "IFileSerializer.h"
namespace Algorithm
{
namespace Interface
{
// Class contract that exposes common methods for which to extend
class ISimpleSerializedType : public virtual IType,public virtual IFileSerializer
{
public:
virtual IPropertyTree ToPropertyTree(void){
IPropertyTree pt;
return SimplePropertyTree(pt,true);
};
// method which extracts the values from property tree
virtual void FromPropertyTree(IPropertyTree& pt){
auto tree = SimplePropertyTree(pt,false);
pt = tree._pt;
};
protected:
// need to implement this
virtual IPropertyTree SimplePropertyTree(IPropertyTree& pt,bool ToPropertyTree)
{
return pt;
}
};
}
}
#endif
Code For ITYPE
#ifndef ITYPE_H
#define ITYPE_H
#include <sstream>
#include <string>
#include <vector>
#include <string>
#include "IPropertyTree.h"
#include <fstream>
// macross to simplify streaming property tree
#define __str__(s) #s
#define PADD(s) {\
try\
{\
std::string ss = std::to_string(s);\
std::string key = std::string(__str__(s));\
pt.add(key,ss);\
}\
catch (std::exception ex)\
{\
}\
}
#define PADDS(s) {\
try\
{\
std::string key = std::string(__str__(s));\
pt.add(key,s);\
}\
catch (std::exception ex)\
{\
}\
}
#define PADDBASE(BASE){\
auto st = std::string(__str__(BASE));\
auto pt2 = BASE##ToPropertyTree();\
pt.addPropertyTree(st, pt2);\
}
#define PADDMEMBER(membervar) {\
auto st = std::string(__str__(membervar));\
LOGIT1(st)\
auto _pt = membervar.ToPropertyTree();\
pt.addPropertyTree(st, _pt);\
}
// PGET
#define PGET(VAR,type) { std::string s(__str__(VAR));\
VAR = pt.get<type>(s); }
#define PGETBASE(VAR) {\
try\
{\
auto st = std::string(__str__(VAR));\
auto ptBase##VAR = pt.getChild(st); \
VAR##FromPropertyTree(ptBase##VAR);\
}\
catch (...)\
{\
}\
}
#define PGETMEMBER(membervar) {\
auto st = std::string(__str__(membervar));\
auto pt2 = pt.getChild(st);\
membervar.FromPropertyTree(pt2);\
}
///////////////
/// PGET2
#define PGET2(VAR,type) { std::string s(__str__(VAR));\
VAR = pt._pt.get<type>(s); }
#define PGET2BASE(VAR) {\
try\
{\
auto st = std::string(__str__(VAR));\
auto ptBase##VAR = pt._pt.getChild(st); \
VAR##FromPropertyTree(ptBase##VAR);\
}\
catch (...)\
{\
}\
}
#define PGET2MEMBER(membervar) {\
auto st = std::string(__str__(membervar));\
auto pt2 = pt_pt.getChild(st);\
membervar.FromPropertyTree(pt2);\
}
// PSerialize uses a implied type bool ToPropertyTree and pt
#define PSER(VAR,type) if(toPropertyTree) {\
std::cout << "padd" << std::endl;\
PADD(VAR)\
} else {\
std::cout << "pget" << std::endl;\
PGET(VAR,type)\
}
#define PSERS(VAR) if(toPropertyTree) {\
PADDS(VAR)\
} else {\
PGET(VAR,std::string)\
}
#define PSERBASE(VAR)if(toPropertyTree) {\
PADDBASE(VAR)\
} else {\
PGET2BASE(VAR)\
}
#define PSERMEMBER(membervar)if(toPropertyTree) {\
PADDMEMBER(membervar) \
} else {\
PGET2MEMBER(membervar) \
}
namespace Algorithm
{
namespace Interface
{
// Class contract that exposes common methods for which to extend
class IType
{
public:
IType() {};
// causes problems with hiberlite when you derive it
// from MVC so omitting this
// IType(IType& rhs) { *this = rhs; }
virtual ~IType(){}; // destructor
// methods don't communicate tho the key just the value
// like stl containers returns size of type
virtual size_t size(void){ return sizeof(IType);};
// says the maximum size of the type
virtual size_t max_size(void) { return sizeof(IType); };
virtual void ToString(char* data,size_t& dataSize){ /* not implemented*/ };
virtual void FromString(char* data,size_t& dataSize){};
IType& operator=(const IType& rhs){
std::string s;
IType& rhsRef = const_cast<IType&>(rhs);
size_t size = rhsRef.size();
s.resize(size);
rhsRef.ToString(const_cast<char*>(s.c_str()), size);
FromString(const_cast<char*>(s.c_str()),size);
return *this;
};
// must be friended methods
// istream extraction operators terminated by std::endl for each respective subtype
// ostream extraction operators terminated by std::endl for each respective subtype
// encode the stream to stream with variable name + value name. Useful for key value streams;
virtual IPropertyTree ToPropertyTree(void){
IPropertyTree pt;
return pt;
};
// method which extracts the values from property tree
virtual void FromPropertyTree(boost::property_tree::ptree& typesEncodedInAPropertyTree){
IPropertyTree pt;
pt._pt = typesEncodedInAPropertyTree;
FromPropertyTree(pt);
};
// method which extracts the values from property tree
virtual void FromPropertyTree(IPropertyTree& typesEncodedInAPropertyTree) {
};
// call a serializer here
// method instructs how to write to file by calling the approppriate serializer
virtual void ToFile(void){
};
virtual void FromFile(void) {};
virtual std::string TypeName(void) { return ""; };
protected:
inline bool exist(const std::string& name)
{
std::ifstream file(name);
if (!file) // If the file was not found, then file is 0, i.e. !file=1 or true.
return false; // The file was not found.
else // If the file was found, then file is non-0.
return true; // The file was found.
}
};
}
}
#endif
Code For IPropertyTree
#ifndef I_PROPERTY_TREE_H
#define I_PROPERTY_TREE_H
#include <boost/property_tree/ptree.hpp>
#include <memory>
#include <map>
#include <string>
#include <vector>
#include <iostream>
namespace Algorithm
{
namespace Interface
{
class IPropertyTree
{
const std::string attributePrefix = ".<xmlattr>."; // attribute prefix to reference a attribute within boost property tree
// https://stackoverflow.com/questions/3690436/how-are-attributes-parsed-in-boost-propertytree
std::string BuildAttributeInsertionKey(std::string& key, std::string& attributeKey) { return key + attributePrefix + attributeKey; };
public:
boost::property_tree::ptree _pt; // good reference reading https://theboostcpplibraries.com/boost.propertytree
const IPropertyTree& operator=(const IPropertyTree& pt){
this->_pt = pt._pt;
return *this;};
IPropertyTree(void) :_pt() {};
IPropertyTree(boost::property_tree::ptree& pt) : _pt(pt) {};
// usually only accessed by the serializers don't manually edit this
boost::property_tree::ptree& GetBoostPropertyTree(void) { return _pt; };
#ifdef _WIN32
// key/value get and set
template <class T>
void add(std::string& key, T& value)
{
_pt.put(key, value);
};
#else
template <class T>
void add(std::string key, T value)
{
_pt.put(key, value);
};
#endif
template <class T>
T get(std::string& path) {
return _pt.get<T>(path);
};
// attribute get/set
template <class T>
void addAttribute(std::string& keyName, std::string& attributeKey, T& attributeValue) {
_pt.add(BuildAttributeInsertionKey(keyName, attributeKey), std::to_string(attributeValue));
}
IPropertyTree getChild(std::string& key)
{
return IPropertyTree(_pt.get_child(key));
}
template <class T>
T getAttribute(std::string& keyPath, std::string& attributeName) {
return _pt.get<T>(BuildAttributeInsertionKey(keyPath, attributeName));
}
void addPropertyTree(std::string& keyOfChildTree,IPropertyTree& tree)
{
_pt.add_child(keyOfChildTree,tree.GetBoostPropertyTree());
};
void addAttribute(std::string& keyName,std::string& attributeKey, std::string& attributeValue)
{
_pt.add(BuildAttributeInsertionKey(keyName,attributeKey), attributeValue);
};
};
}
}
#endif
Code For IFileSerializer
#ifndef I_FILE_SERIALIZER_H
#define I_FILE_SERIALIZER_H
#include "IJSONSerialize.h"
#include "IType.h"
#include "../../Tools/Diagnostics/Logger/Logger.h" // this uses LOGIT but you can just replace with std::cout
#include <cstdint>
#include <cstdlib>
#include <string>
namespace Algorithm
{
namespace Interface
{
class IFileSerializer;
// a Serializer for JSON
class IFileSerializer : public virtual Algorithm::Interface::IType
{
public:
std::string filename;
IFileSerializer(void):Algorithm::Interface::IType(),filename(){};
virtual void ToFile(void)
{
std::string msg = TypeName() + "::ToFile()";
LOGIT1(msg)
std::string testJSON(filename);
auto pt = ToPropertyTree();
msg = TypeName() + "::ToFile() calling IJSON serialize";
LOGIT1(msg)
Algorithm::Interface::IJSONSerialize test(testJSON, pt);
msg = TypeName() + "::ToFile() WriteFile";
LOGIT1(msg)
test.WriteFile();
};
virtual void FromFile(void)
{
auto msg = TypeName() + "::FromFile()\n";
LOGIT1(msg)
std::string testJSON(filename);
auto pt = ToPropertyTree();
Algorithm::Interface::IJSONSerialize test(testJSON, pt);
test.ReadFile();
this->FromPropertyTree(test.GetPropertyTree());
};
virtual Algorithm::Interface::IPropertyTree ToPropertyTree(void) { Algorithm::Interface::IPropertyTree pt; return pt;};
// method which extracts the values from property tree
virtual void FromPropertyTree(Algorithm::Interface::IPropertyTree& pt) {};
void ParseServerArgs(char** argv, int argc){
std::string msg2="IFileSerializer::ParseServerArgs";
LOGIT1(msg2)
filename = "config.json";
if(exist(filename))
{
std::string msg = "IFileSerializer::Calling FromFile";
LOGIT1(msg)
FromFile();
}
else
{
std::string msg = "IFileSerializer::Calling ToFile";
LOGIT1(msg)
ToFile(); // write it back so next time you can feed in the json
}
};
}; // end class
}
}
#endif
IJSONSerialize Code
#ifndef IJSONSERIALIZE_H
#define IJSONSERIALIZE_H
#include <string>
#include <vector>
#include <iostream>
#include <boost/property_tree/json_parser.hpp>
#include "IPropertyTree.h"
namespace Algorithm
{
namespace Interface
{
// object that provides facilities to serialize JavaScript Object Notation(JSON)
// citation: https://stackoverflow.com/questions/4586768/how-to-iterate-a-boost-property-tree
class IJSONSerialize
{
IPropertyTree _pt;
std::string _filename;
public:
IJSONSerialize(const std::string& filename, IPropertyTree& pt):_pt(pt),_filename(filename){
};
virtual void WriteFile(void){
try
{
boost::property_tree::json_parser::write_json(_filename, _pt.GetBoostPropertyTree());
}
catch(std::exception ex)
{
std::cerr << "can't write json file " << _filename;
}
};
virtual void WriteAsAString(std::string& outString)
{
std::stringstream ss;
boost::property_tree::write_json(ss, _pt.GetBoostPropertyTree());
outString = ss.str();
};
virtual void ReadFile(void){
try
{
boost::property_tree::read_json(_filename, _pt.GetBoostPropertyTree());
}
catch(const boost::property_tree::json_parser_error &jpe)
{
//do error handling
std::cerr << "can't read json file " << _filename <<jpe.what();
}
};
virtual void ReadFromString(std::string& s){
try
{
std::stringstream ss;
ss << s;
auto pt = _pt.GetBoostPropertyTree(); boost::property_tree::json_parser::read_json(ss, pt);
}
catch(std::exception)
{
}
};
virtual std::string WriteToString(void){
std::stringstream ss;
boost::property_tree::json_parser::write_json(ss,_pt.GetBoostPropertyTree());
return ss.str();
};
// use to retrieve all the values but
virtual IPropertyTree& GetPropertyTree(void){
return _pt;
};
};
}
}
#endif
If any code missing you can find it in my bitbucket crossplatform C++ network template that's built
on top of boost asio. The code is here: https://bitbucket.org/ptroen/crossplatformnetwork/src/master/
And again if you missed the comment and don't want to use LOGIT you can just find and replace with std::cout
Note code above is working but if you study enough their is some tech debt that could be optimized even more like reflection
Anyways hope you find this useful

Related

Decision Tree using data mining technique to determine whether a tumor is benign or malignant

I have to read patient data from a .csv file and using a decision tree determine, based on the data being read in for each patient, whether the tumor is Benign or Malignant.
I am really struggling with how to even start this. So far I have written code that reads from the .csv file and stores the data into a vector as shown below spreading over a few header and cpp files.
From what I gather, I can create a parent decision class and then each attribute I am to process are the children classes. Not sure if that makes sense. Please let me know.
Below you will find the attributes I am to process along with a graphical tree that shows how it is determined whether the tumor is Benign or Malignant that I need to base my code off of. I will also include a small sample of the .csv file.
Please could I get some guidance as how I am to do this. I am having the greatest difficulty with pointer notation. Any guidance will greatly be appreciated.
CSVLine.h
#ifndef CSVLINE_H
#define CSVLINE_H
#include <string>
#include <sstream>
#include <vector>
using namespace std;
class CSVLine
{
private:
vector<string> data;
public:
CSVLine() {}
CSVLine(const CSVLine& other)
{
data = other.data;
}
CSVLine operator = (const CSVLine& other)
{
data = other.data;
}
~CSVLine() {}
void parse(string line, char delimiter = ',');
string getString(int columnNumber);
int getInt(int columnNumber);
};
#endif
CSVLine.cpp
#include "CSVLine.h"
void CSVLine::parse(string line, char delimiter)
{
stringstream inLine(line);
string tempColumn = "";
while (getline(inLine, tempColumn, delimiter))
{
data.push_back(tempColumn);
}
}
string CSVLine::getString(int columnNumber)
{
return data[columnNumber];
}
int CSVLine::getInt(int columnNumber)
{
return atoi(data[columnNumber].c_str());
}
CSVReader.h
#ifndef CSVREADER_H
#define CSVREADER_H
#include <vector>
#include <fstream>
#include <iostream>
#include "CSVLine.h"
using namespace std;
class CSVReader
{
public:
CSVReader() {}
vector<CSVLine> read(string fileName);
};
#endif
CSVReader.cpp
#include "CSVReader.h"
vector<CSVLine> CSVReader::read(string fileName)
{
ifstream inputFile;
vector<CSVLine> lines;
inputFile.open(fileName.c_str());
string line = "";
while (getline(inputFile, line))
{
CSVLine csvLine;
csvLine.parse(line);
lines.push_back(csvLine);
}
return lines;
}
Here is what I would do.
First, I would translate the table of features to a higher-order macro:
#define FOREACH_FEATURE(OP) \
OP(1, SampleCodeNumber, int, -1) \
OP(2, ClumpThickness, int, -1) \
OP(3, UniformityOfCellSize, int, -1)
// Fill in the rest of the table of features here yourself
Then I would use this macro to generate a struct with all the features of a patient like this:
struct PatientData {
#define DECL_FEATURE(index, name, type, init) type name = init;
FOREACH_FEATURE(DECL_FEATURE)
#undef DECL_FEATURE
PatientData() {}
PatientData(CSVLine& src) {
#define READ_FEATURE(index, name, type, init) name = src.getInt(index-1);
FOREACH_FEATURE(READ_FEATURE)
#undef READ_FEATURE
}
};
Then I would construct a PatientData object from a CSVLine:
CSVLine line = ...;
PatientData patientData(line);
Then I would implement the decision tree as nested if-statements on the patientData object:
if (patientData.UniformityOfCellSize <= 2) {
// ...
} else {
// ...
}
This would get you started but you need to complete and possible extend the FOREACH_FEATURE macro and implement the decision tree...
Nodes and pointers approach
If you don't want to implement your tree like above, ditch the above code and instead do the following. Start by including a few files that we need and implement a Feature class:
#include <memory>
#include <functional>
struct Feature {
int index1;
int apply(CSVLine& line) const {return line.getInt(index1-1);}
};
and translate the table of features to Feature like this:
Feature SampleCodeNumber{1};
Feature ClumpThickness{2};
Feature UniformityOfCellSize{3};
// Fill in the rest yourself
We are going to use an std::function<bool(CSVLine)> to decide for the branch in the tree:
typedef std::function<bool(CSVLine&)> BranchCondition;
Overloading the comparison operator for a Feature and double to return a BranchCondition lets us neatly express BranchConditions:
#define DEF_FEATURE_OP(op) BranchCondition operator op (Feature f, double x) {return [f, x](CSVLine& line) {return f.apply(line) op x;};}
DEF_FEATURE_OP(<)
DEF_FEATURE_OP(<=)
DEF_FEATURE_OP(>)
DEF_FEATURE_OP(>=)
#undef DEF_FEATURE_OP
We also need to declare the return value of the classification:
enum class Severity {
Benign, Malign
};
As a base class for the decision tree we declare
class PatientClassifier {
public:
virtual Severity classify(CSVLine& p) const = 0;
virtual ~PatientClassifier() {}
};
and implement it for the trivial case of a constant value along with a function severity to construct it:
class ConstantClassifier : public PatientClassifier {
public:
ConstantClassifier(Severity v) : _value(v) {}
Severity classify(CSVLine&) const override {return _value;}
private:
Severity _value;
};
std::shared_ptr<PatientClassifier> severity(Severity v) {
return std::make_shared<ConstantClassifier>(v);
}
and for the branching case along with a function branch:
class BranchingClassifier : public PatientClassifier {
public:
BranchingClassifier(
BranchCondition f,
const std::shared_ptr<PatientClassifier>& onTrue,
const std::shared_ptr<PatientClassifier>& onFalse)
: _f(f), _onTrue(onTrue), _onFalse(onFalse) {}
Severity classify(CSVLine& p) const override {
return (_f(p)? _onTrue : _onFalse)->classify(p);
}
private:
BranchCondition _f;
std::shared_ptr<PatientClassifier> _onTrue;
std::shared_ptr<PatientClassifier> _onFalse;
};
std::shared_ptr<PatientClassifier> branch(
BranchCondition f,
const std::shared_ptr<PatientClassifier>& onTrue,
const std::shared_ptr<PatientClassifier>& onFalse) {
return std::make_shared<BranchingClassifier>(f, onTrue, onFalse);
}
and then we just build the tree like
auto decisionTree = branch(
UniformityOfCellSize <= 2.0,
severity(Severity::Benign),
severity(Severity::Malign));
CSVLine line;
auto result = decisionTree->classify(line);
Note: You don't need custom copy constructor and assignment operator for CSVLine. And the getInt method could be marked as const.

Using nlohmann fifo_map with base class and derived class

I am currently trying to generate JSON with ordered keys and therefore used a workaround method. However, if I try to use it within my base and derived classes, I get an error which I do not really understand. It seems like it fails to call the to_Json methods (because the error appears if I try to map a DerivedClass-instance (test and test2) to my_json.
I have already tried the example without ordered keys (just by using json = nlohmann::json;) and it works completely fine. The keys in the output are sorted alphabetically and looks like this:
{
"cbor": "cbortest",
"diagnostic": "diagnose: corona",
"header": {
"headerId": 3,
"timestamp": "2019-12-10T16:04:00.00Z",
"version": "4.0.0"
},
"hex": "00f2",
"roundtrip": true
}
What I am trying to achieve through using the nlohmann fifo_map is to keep the insertion order and the final output therefore should look like this:
{
"header": {
"headerId": 3,
"timestamp": "2019-12-10T16:04:00.00Z",
"version": "4.0.0"
},
"cbor": "cbortest",
"hex": "00f2",
"roundtrip": true,
"diagnostic": "diagnose: corona"
}
Executing the following code outputs two errors:
Error C2440: 'initializing': cannot convert from 'BaseNamespace::SubNamespace::DerivedClass' to 'nlohmann::basic_json<my_workaround_fifo_map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>' ; in file: main.cpp
Please have a look at the following code:
In BaseClass.h:
#ifndef BASECLASS_H
#define BASECLASS_H
#include <stdint.h>
#include <string>
#include "nlohmann/json.hpp"
#include "fifo_map.hpp"
namespace BaseNamespace{
namespace SubNamespace{
class BaseClass {
public:
BaseClass () {};
virtual ~BaseClass () {};
uint32_t getHeaderId() const { return headerId; };
std::string getTimestamp() const { return timestamp; };
std::string getVersion() const { return version; };
void setHeaderId(uint32_t str) { headerId = str; };
void setTimestamp(std::string str) { timestamp = str; };
void setVersion(std::string bo) { version = bo; };
void setHeader(UAgvHeader const& header) {
setHeaderId(header.getHeaderId());
setTimestamp(header.getTimestamp());
setVersion(header.getVersion());
}
private:
uint32_t headerId;
std::string timestamp;
std::string version;
};
// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
using namespace nlohmann;
template<class K, class V, class dummy_compare, class A>
using my_workaround_fifo_map = fifo_map<K, V, fifo_map_compare<K>, A>;
using my_json = basic_json<my_workaround_fifo_map>;
void to_json(my_json &j, const BaseClass &p)
{
j = my_json{
{ "headerId", p.getHeaderId() },
{ "timestamp", p.getTimestamp() },
{ "version", p.getVersion() }
};
}
void from_json(const my_json &j, BaseClass &p)
{
p.setHeaderId(j.at("headerId").get< std::uint32_t>());
p.setTimestamp(j.at("timestamp").get< std::string >());
p.setVersion(j.at("version").get<std::string>());
}
} // namespace SubNamespace
} // namespace BaseNamespace
#endif // BASECLASS_H_
In DerivedClass.h:
#ifndef DERIVEDCLASS_H
#define DERIVEDCLASS_H
#include <stdint.h>
#include <string>
#include "nlohmann/json.hpp"
#include <optional>
#include "BaseClass.h"
namespace BaseNamespace{
namespace SubNamespace{
class DerivedClass : public BaseClass {
public:
std::string getCBor() const { return cbor; };
std::string getHex() const { return hex; };
bool getRoundtrip() const { return roundtrip; };
std::optional<std::string> getDiagnostic() const { return diagnostic; };
void setCBor(std::string str) { cbor = str; };
void setHex(std::string str) { hex = str; };
void setRoundtrip(bool bo) { roundtrip = bo; };
void setDiagnostic(std::optional<std::string> opt_str) { diagnostic = opt_str; };
private:
std::string cbor;
std::string hex;
bool roundtrip;
std::optional<std::string> diagnostic = std::nullopt;
};
// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
using namespace nlohmann;
template<class K, class V, class dummy_compare, class A>
using my_workaround_fifo_map = fifo_map<K, V, fifo_map_compare<K>, A>;
using my_json = basic_json<my_workaround_fifo_map>;
void to_json(my_json &j, const DerivedClass& p)
{
j["header"] = static_cast<BaseClass>(p);
j["cbor"] = p.getCBor();
j["hex"] = p.getHex();
j["roundtrip"] = p.getRoundtrip();
// assuming you only want a "diagnostic" key if there is an actual value;
// if not, store a nullptr and adjust the from_json accordingly
if (p.getDiagnostic() != std::nullopt)
{
j["diagnostic"] = p.getDiagnostic().value();
}
}
void from_json(const my_json &j, DerivedClass&p)
{
p.setHeader(j.at("header").get<BaseClass>());
p.setCBor(j.at("cbor").get< std::string >());
p.setHex(j.at("hex").get< std::string >());
p.setRoundtrip(j.at("roundtrip").get< bool >());
// if we also allow "null" values, then we need to add an "is_string()"
// check
if (j.count("diagnostic") != 0)
{
p.setDiagnostic(j.at("diagnostic").get< std::string >());
}
}
} // namespace SubNamespace
} // namespace BaseNamespace
#endif // DERIVEDCLASS_H
In main.cpp:
#include <iostream>
#include <string>
#include <nlohmann/json.hpp>
#include <iomanip>
#include <optional>
#include "DerivedClass.h"
using namespace nlohmann;
// A workaround to give to use fifo_map as map, we are just ignoring the 'less' compare
template<class K, class V, class dummy_compare, class A>
using my_workaround_fifo_map = fifo_map<K, V, fifo_map_compare<K>, A>;
using my_json = basic_json<my_workaround_fifo_map>;
int main(int argc, char* argv[]) {
BaseNamespace::SubNamespace::DerivedClass test;
test.setHeaderId(3);
test.setTimestamp("2019-12-10T16:04:00.00Z");
test.setVersion("4.0.0");
test.setCBor("cbortest");
test.setHex("00f2");
test.setRoundtrip(true);
test.setDiagnostic("diagnose: corona");
my_json j = test; // ERROR: no suitable conversion
std::cout << std::setw(2) << j << std::endl;
std::string str = R"({"header":
{ "headerId" : 4711,
"timestamp" : "1 Uhr",
"version" : "5.0.0"
},
"cbor" : "+X4A",
"hex" : "f97e00",
"roundtrip" : true,
"diagnostic" : "NaN"
})";
my_json j2 = my_json::parse(str);
BaseNamespace::SubNamespace::DerivedClass test2 = j2;
my_json k = test2; // ERROR: no suitable conversion
std::cout << std::setw(2) << k << std::endl;
return 0;
}

C++ SFML Gamedev Book - Unresolved External Symbol from ResourceHolder class

I have the following three files, of which I cannot find the source of an error that it is producing:
Main.cpp
#include <SFML/Graphics.hpp>
#include <iostream>
#include "ResourceHolder.h"
namespace Textures
{
enum ID { Landscape, Airplane, Missile };
}
int main()
{
//...
try
{
ResourceHolder<sf::Texture, Textures::ID> textures;
textures.load(Textures::Airplane, "Airplane.png");
}
catch (std::runtime_error& e)
{
std::cout << "Exception: " << e.what() << std::endl;
}
//...
}
ResourceHolder.h
#pragma once
#include <map>
#include <string>
#include <memory>
#include <stdexcept>
#include <cassert>
template <typename Resource, typename Identifier>
class ResourceHolder
{
public:
void load(Identifier id, const std::string& fileName);
Resource& get(Identifier id);
const Resource& get(Identifier id) const;
private:
void insertResource(Identifier id, std::unique_ptr<Resource> resource);
std::map<Identifier, std::unique_ptr<Resource>> mResourceMap;
};
ResourceHolder.cpp
#include "ResourceHolder.h"
template <typename Resource, typename Identifier>
void ResourceHolder<Resource, Identifier>::load(Identifier id, const std::string& fileName)
{
//Create and load resource
std::unique_ptr<Resource> resource(new Resource());
if (!resource->loadFromFile(fileName)) {
throw std::runtime_error("ResourceHolder::load - Failed to load " + fileName);
}
//If loading was successful, insert resource to map
insertResource(id, std::move(resource));
}
template <typename Resource, typename Identifier>
Resource& ResourceHolder<Resource, Identifier>::get(Identifier id)
{
auto found = mResourcemap.find(id);
assert(found != mResourceMap.end());
return *found->second();
}
template <typename Resource, typename Identifier>
void ResourceHolder<Resource, Identifier>::insertResource(Identifier id, std::unique_ptr<Resource> resource)
{
//Insert and check success
auto inserted = mResourceMap.insert(std::make_pair(id, std::move(resource)));
assert(inserted.second);
}
If I were to remove the try-catch combination in main.cpp, the code compiles fine; However, if I leave it there it gives me an LNK2019 (Unresolved external symbol) Error.
What is the source of this error, and how would I fix it?
You can't define templates inside .cpp files. They have to be defined in the header so the compiler can see the implementation and generate the specific classes.
Here's a better question/answer on why it is so Why can templates only be implemented in the header file?.
EDIT: What's wrong in the get function
Two things.
First is this auto found = mResourcemap.find(id);. Your map name is incorrect, m should be upper case -> mResourceMap.
Then the line return *found->second();. The map iterator contains a pair, and the first and second members are not functions but data members. You should write return *found->second;.
I would advise you to understand the structures you're working with before using templates. The compile errors with templates are pretty messy and harder to read. Also you could make a separate test program and make a resource manager with no templates to understand your errors more easily, then build the template on top of your working resource manager.
With all the other answers providing you with enough information to why your code don't compile and might not be valid, this is a resource manager i wrote for SFML some time ago, might be of use to you:
HPP FILE:
#ifndef RESOURCEMANAGER_HPP
#define RESOURCEMANAGER_HPP
/************ INCLUDES ***********/
#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <memory>
#include "SFML/Graphics.hpp"
#include "SFML/Audio.hpp"
class ResourceManager
{
private:
std::map<std::string,std::unique_ptr<sf::Texture>> listImageContainer;
std::map<std::string,std::pair<std::unique_ptr<sf::SoundBuffer>,std::unique_ptr<sf::Sound>>> listSoundContainer;
std::map<std::string,std::unique_ptr<sf::Font>> listFontContainer;
public:
ResourceManager();
std::unique_ptr<sf::Sound>& LoadSound(const std::string);
std::unique_ptr<sf::Font>& LoadFont(const std::string);
std::unique_ptr<sf::Texture>& LoadImage(const std::string);
~ResourceManager();
};
#endif
CPP FILE:
#include "ResourceManager.hpp"
ResourceManager::ResourceManager()
{
}
std::unique_ptr<sf::Sound>& ResourceManager::LoadSound(const std::string _fileName)
{
if (listSoundContainer.find(_fileName) == listSoundContainer.end())
{
std::unique_ptr<sf::SoundBuffer> soundBuffer(new sf::SoundBuffer());
if (soundBuffer->loadFromFile("assets/sound/" + _fileName) != false)
{
std::unique_ptr<sf::Sound> sound(new sf::Sound(*soundBuffer));
listSoundContainer[_fileName] = std::make_pair(std::move(soundBuffer), std::move(sound));
return listSoundContainer[_fileName].second;
}
else
{
std::cerr << "Error loading sound..." << std::endl;
}
}
else
{
return listSoundContainer[_fileName].second;
}
}
std::unique_ptr<sf::Font>& ResourceManager::LoadFont(const std::string _fileName)
{
if (listFontContainer.find(_fileName) == listFontContainer.end())
{
std::unique_ptr<sf::Font> font(new sf::Font());
if (font->loadFromFile("assets/font/" + _fileName)!=false)
{
listFontContainer[_fileName] = std::move(font);
return listFontContainer[_fileName];
}
else
{
std::cerr << "Error loading font..." << std::endl;
}
}
else
{
return listFontContainer[_fileName];
}
}
std::unique_ptr<sf::Texture>& ResourceManager::LoadImage(const std::string _fileName)
{
if (listImageContainer.find(_fileName) == listImageContainer.end())
{
std::unique_ptr<sf::Texture> texture(new sf::Texture);
if (texture->loadFromFile("assets/image/" + _fileName)!=false)
{
listImageContainer[_fileName] = std::move(texture);
return listImageContainer[_fileName];
}
else
{
std::cerr << "Error loading image: " << _fileName << std::endl;
}
}
else
{
return listImageContainer[_fileName];
}
}
ResourceManager::~ResourceManager(){}
How to use:
ResourceManager resourceManager;
auto& sound = resourceManager.LoadSound("nice.wav");
auto& image = resourceManager.LoadImage("head.png");
auto& sound2 = resourceManager.LoadSound("nice.wav"); //<--- already loaded
sound.play();
etc...

C++ How do you output an object's type [duplicate]

Is it possible to get the object name too?
#include<cstdio>
class one {
public:
int no_of_students;
one() { no_of_students = 0; }
void new_admission() { no_of_students++; }
};
int main() {
one A;
for(int i = 0; i < 99; i++) {
A.new_admission();
}
cout<<"class"<<[classname]<<" "<<[objectname]<<"has "
<<A.no_of_students<<" students";
}
where I can fetch the names, something like
[classname] = A.classname() = one
[objectname] = A.objectname() = A
Does C++ provide any mechanism to achieve this?
You can display the name of a variable by using the preprocessor. For instance
#include <iostream>
#define quote(x) #x
class one {};
int main(){
one A;
std::cout<<typeid(A).name()<<"\t"<< quote(A) <<"\n";
return 0;
}
outputs
3one A
on my machine. The # changes a token into a string, after preprocessing the line is
std::cout<<typeid(A).name()<<"\t"<< "A" <<"\n";
Of course if you do something like
void foo(one B){
std::cout<<typeid(B).name()<<"\t"<< quote(B) <<"\n";
}
int main(){
one A;
foo(A);
return 0;
}
you will get
3one B
as the compiler doesn't keep track of all of the variable's names.
As it happens in gcc the result of typeid().name() is the mangled class name, to get the demangled version use
#include <iostream>
#include <cxxabi.h>
#define quote(x) #x
template <typename foo,typename bar> class one{ };
int main(){
one<int,one<double, int> > A;
int status;
char * demangled = abi::__cxa_demangle(typeid(A).name(),0,0,&status);
std::cout<<demangled<<"\t"<< quote(A) <<"\n";
free(demangled);
return 0;
}
which gives me
one<int, one<double, int> > A
Other compilers may use different naming schemes.
use typeid(class).name
// illustratory code assuming all includes/namespaces etc
#include <iostream>
#include <typeinfo>
using namespace std;
struct A{};
int main(){
cout << typeid(A).name();
}
It is important to remember that this
gives an implementation defined names.
As far as I know, there is no way to get the name of the object at run time reliably e.g. 'A' in your code.
EDIT 2:
#include <typeinfo>
#include <iostream>
#include <map>
using namespace std;
struct A{
};
struct B{
};
map<const type_info*, string> m;
int main(){
m[&typeid(A)] = "A"; // Registration here
m[&typeid(B)] = "B"; // Registration here
A a;
cout << m[&typeid(a)];
}
To get class name without mangling stuff you can use func macro in constructor:
class MyClass {
const char* name;
MyClass() {
name = __func__;
}
}
Do you want [classname] to be 'one' and [objectname] to be 'A'?
If so, this is not possible. These names are only abstractions for the programmer, and aren't actually used in the binary code that is generated. You could give the class a static variable classname, which you set to 'one' and a normal variable objectname which you would assign either directly, through a method or the constructor. You can then query these methods for the class and object names.
Just write simple template:
template<typename T>
const char* getClassName(T) {
return typeid(T).name();
}
struct A {} a;
void main() {
std::cout << getClassName(a);
}
You could try using "typeid".
This doesn't work for "object" name but YOU know the object name so you'll just have to store it somewhere. The Compiler doesn't care what you namned an object.
Its worth bearing in mind, though, that the output of typeid is a compiler specific thing so even if it produces what you are after on the current platform it may not on another. This may or may not be a problem for you.
The other solution is to create some kind of template wrapper that you store the class name in. Then you need to use partial specialisation to get it to return the correct class name for you. This has the advantage of working compile time but is significantly more complex.
Edit: Being more explicit
template< typename Type > class ClassName
{
public:
static std::string name()
{
return "Unknown";
}
};
Then for each class somethign liek the following:
template<> class ClassName<MyClass>
{
public:
static std::string name()
{
return "MyClass";
}
};
Which could even be macro'd as follows:
#define DefineClassName( className ) \
\
template<> class ClassName<className> \
{ \
public: \
static std::string name() \
{ \
return #className; \
} \
}; \
Allowing you to, simply, do
DefineClassName( MyClass );
Finally to Get the class name you'd do the following:
ClassName< MyClass >::name();
Edit2: Elaborating further you'd then need to put this "DefineClassName" macro in each class you make and define a "classname" function that would call the static template function.
Edit3: And thinking about it ... Its obviously bad posting first thing in the morning as you may as well just define a member function "classname()" as follows:
std::string classname()
{
return "MyClass";
}
which can be macro'd as follows:
DefineClassName( className ) \
std::string classname() \
{ \
return #className; \
}
Then you can simply just drop
DefineClassName( MyClass );
into the class as you define it ...
You can try this:
template<typename T>
inline const char* getTypeName() {
return typeid(T).name();
}
#define DEFINE_TYPE_NAME(type, type_name) \
template<> \
inline const char* getTypeName<type>() { \
return type_name; \
}
DEFINE_TYPE_NAME(int, "int")
DEFINE_TYPE_NAME(float, "float")
DEFINE_TYPE_NAME(double, "double")
DEFINE_TYPE_NAME(std::string, "string")
DEFINE_TYPE_NAME(bool, "bool")
DEFINE_TYPE_NAME(uint32_t, "uint")
DEFINE_TYPE_NAME(uint64_t, "uint")
// add your custom types' definitions
And call it like that:
void main() {
std::cout << getTypeName<int>();
}
An improvement for #Chubsdad answer,
//main.cpp
using namespace std;
int main(){
A a;
a.run();
}
//A.h
class A{
public:
A(){};
void run();
}
//A.cpp
#include <iostream>
#include <typeinfo>
void A::run(){
cout << (string)typeid(this).name();
}
Which will print:
class A*
Here is a trick for getting the name of a class you create:
struct NameTest {
NameTest() : mName {std::source_location::current().function_name()} {
}
void operator()() {
auto src_loc = std::source_location::current();
std::cout << "Class name:\t" << mName //
<< "\nFunc:\t\t" << src_loc.function_name() //
<< "\nLine:\t\t" << src_loc.line() << '\n';
}
const std::string mName;
};
int main() {
NameTest name_test;
name_test();
return 0;
}
output:
Class name: NameTest::NameTest()
Func: void NameTest::operator()()
Line: 81
A little string manipulation will strip the unneeded parts

Function refuses to work in Boost test function

I cant understand why in the class constructor I can call this function but when called in the test function, it errors out with
E:\Projects\NasuTek-Plugin-Engine\tests\CheckAddonEngine.cpp:64: error: conversion from 'std::auto_ptr<FakeSettableFeaturePlugin>' to 'std::auto_ptr<FakeFeature>' is ambiguous
C++ File
#include <ObjectEngine.h>
#include <memory>
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
class FakeFeature : public PObject {
public:
inline virtual const char *returnSomthingCool() { return "Somthing Cool"; }
};
class FakeFeaturePlugin : public FakeFeature {
public:
inline const char *returnSomthingCool() { return "Somthing Cool From a Plugin Object"; }
inline std::string getName() { return "Fake Feature Plugin Object"; }
};
class FakeSettableFeaturePlugin : public FakeFeature {
public:
inline const char *returnSomthingCool() { return _value; }
inline char *setSomthingCool(const char *value) { _value = const_cast<char *>(value); }
inline std::string getName() { return "Fake Feature Settable Plugin Object"; }
private:
char *_value;
};
typedef ObjectEngine<FakeFeature> FakeFeatureEngine;
class FakeApplication {
public:
inline FakeApplication(){
_engine = new FakeFeatureEngine();
std::auto_ptr<FakeFeature> pluginToAdd (new FakeFeaturePlugin);
_engine->addObject(pluginToAdd); // Essencially what im doing where it errors, both classes inherit FakeFeature
}
inline ~FakeApplication() {
delete _engine;
}
FakeFeatureEngine *_engine;
};
BOOST_AUTO_TEST_CASE(getengineobject) {
FakeApplication *application = new FakeApplication();
FakeFeature &feature = application->_engine->getObject("Fake Feature Plugin Object");
BOOST_CHECK_EQUAL(feature.getName(), "Fake Feature Plugin Object");
BOOST_CHECK_EQUAL(feature.returnSomthingCool(), "Somthing Cool From a Plugin Object");
delete application;
}
BOOST_AUTO_TEST_CASE(addobjecttoengine) {
FakeApplication *application = new FakeApplication();
std::auto_ptr<FakeSettableFeaturePlugin> plugin (new FakeSettableFeaturePlugin);
plugin.get()->setSomthingCool("Bro I Set this to this value ^_^");
application->_engine->addObject(plugin); // This is the line that fails
FakeFeature &feature = application->_engine->getObject("Fake Feature Settable Plugin Object");
BOOST_CHECK_EQUAL(feature.getName(), "Fake Feature Settable Plugin Object");
BOOST_CHECK_EQUAL(feature.returnSomthingCool(), "Bro I Set this to this value ^_^");
delete application;
}
.h File
/**
#file ObjectEngine.h
#brief Header File with Plugin Engine Templates to make adding a plugin interface to your app easier
*/
#ifndef NASUTEKPLUGINENGINE_H
#define NASUTEKPLUGINENGINE_H
#include <boost/ptr_container/ptr_list.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <stdio.h>
#include <string>
class PObject {
public:
inline PObject() {}
virtual std::string getName() = 0;
};
template<typename TObjectType>
class ObjectEngine {
public:
ObjectEngine() {}
~ObjectEngine() {
_objects.clear();
}
void addObject(std::auto_ptr<TObjectType> obj) {
if(boost::is_base_of<PObject, TObjectType>::value) {
_objects.push_back(obj.release());
}
}
TObjectType &getObject(std::string objectName) {
typename boost::ptr_list<TObjectType>::iterator i;
for(i = _objects.begin(); i != _objects.end(); i++) {
if((*i).getName() == objectName) {
return *i;
}
}
throw "Object does not exist.";
}
bool objectExist(std::string objectName) {
typename boost::ptr_list<TObjectType *>::iterator i;
for(i = _objects.begin(); i != _objects.end(); i++) {
if((*i).getName() == objectName) {
return true;
}
}
return false;
}
private:
boost::ptr_list<TObjectType> _objects;
};
#endif // NASUTEKPLUGINENGINE_H
What I'm trying to do is create a plugin engine for my projects while making it reusable, and the C++ file is making sure its working right.
This doesn't look like a problem with Boost Test, per se, but it looks like the line in addobjecttoengine...
std::auto_ptr<FakeSettableFeaturePlugin> plugin (new FakeSettableFeaturePlugin);
...should be...
std::auto_ptr<FakeFeature> plugin(new FakeSettableFeaturePlugin);
...the same as in FakeApplication's constructor. Then there's no conversion to perform at CheckAddonEngine.cpp:64.