I would like to create a class and enum to handle errors in my project. As of now I am doing it in the below way.
enum class eErrorType
{
eJsonFileNotFound = 0,
eJsonInvalidFormat,
eJsonKeyNotFound,
eJsonEmptyArray,
eNoError,
eCustom
};
class Error
{
public:
// Constructors
Error() { errorType = eErrorType::eNoError; message = ""; }
Error(eErrorType type) { errorType = type; SetMessage(type); }
Error(std::string msg) { errorType = eErrorType::eCustom; message = msg; }
// Public Methods
std::string getErrMessage() { return message; }
private:
eErrorType errorType;
std::string message;
void SetMessage(eErrorType type)
{
switch (type)
{
case eErrorType::eJsonFileNotFound: message = "Json file not found"; break;
case eErrorType::eJsonInvalidFormat: message = "Invalid json file"; break;
case eErrorType::eJsonKeyNotFound: message = "Specified key is not found in json"; break;
case eErrorType::eJsonEmptyArray: message = "No elements in json array"; break;
case eErrorType::eNoError: message = "Entry contained an attempt to divide by zero!"; break;
default: message = ""; break;
}
}
};
int main()
{
try
{
//open json file. If file open failed, throw error
throw eErrorType::eJsonFileNotFound;
//parse json file. If parsing failed, throw error
throw eErrorType::eJsonInvalidFormat;
//check for particular key in the json. If not found, throw error
throw eErrorType::eJsonKeyNotFound;
}
catch (eErrorType errCode)
{
Error errObj(errCode);
std::cout << errObj.getErrMessage() << std::endl;
}
return 0;
}
I would like some suggestions for improvements. Is there any better way of doing it or any language based features are available to achieve this.
For custom errors you can inherit from std::exception, override exception methods and implement your own stuff, example:
#include <exception> // std::exception
//
// custom exception class
//
class error final :
public std::exception
{
public:
error(const char* description, short code = -1) throw() :
description(description), code(code) { }
const char* what() const throw() override
{
return description;
}
short Code() const throw()
{
return code;
}
error(error&& ref)
: description(ref.description), code(ref.code) { }
error& operator=(error&&)
{
return *this;
}
error(const error& ref)
: description(ref.description), code(ref.code) { }
private:
const char* description;
const short code;
error& operator=(const error&) = delete;
};
Define a macro to show the filename where the error occured:
#include <cstring> // std::strrchr
// Show only file name instead of full path
#define __FILENAME__ (std::strrchr(__FILE__, '\\') ? std::strrchr(__FILE__, '\\') + 1 : __FILE__)
Define universal function to show the error (followin implemetation shows message box but you can redefine it for console program)
#include <string>
#include <windows.h>
#include <codecvt> // string conversion std::wstring_convert and std::codecvt_utf8
//
// converts string or const char* to wstring
//
std::wstring stringToWstring(const std::string& t_str)
{
//setup converter
typedef std::codecvt_utf8<wchar_t> convert_type;
std::wstring_convert<convert_type, wchar_t> converter;
//use converter (.to_bytes: wstr->str, .from_bytes: str->wstr)
return converter.from_bytes(t_str);
}
//
// Set error message to your liking using error class
// and show message box, this function is also used to pass in
// std::exception objects
//
template <typename ExceptionClass>
void ShowError(
HWND hWnd,
ExceptionClass exception,
const char* file,
int line,
long info = MB_ICONERROR)
{
std::string error_type = TEXT("Rutime Error");
std::string error_message = TEXT("File:\t");
#ifdef UNICODE
error_message.append(stringToWstring(file));
#else
error_message.append(file);
#endif // UNICODE
error_message.append(TEXT("\r\nLine:\t"));
error_message.append(std::to_string(line));
error_message.append(TEXT("\r\nError:\t"));
#ifdef UNICODE
error_message.append(stringToWstring(exception.what()));
#else
error_message.append(exception.what());
#endif // UNICODE
// Show error message
MessageBox(hWnd,
error_message.c_str(),
error_type.c_str(), static_cast<UINT>(MB_OK | info));
}
You then show the error like this:
ShowError(nullptr, error("You error message"), __FILENAME__, __LINE__);
For Win32/COM error types the function can be overloaded like this:
#include <comdef.h> // _com_error
#include <windows.h>
#include <string>
//
// Format error code into a string and show message box
// COM and System errors
//
void ShowError(HWND hWnd, const char* file, int line, HRESULT hr = S_OK)
{
string error_type = TEXT("Rutime Error");
string error_message = TEXT("File:\t");
#ifdef UNICODE
error_message.append(stringToWstring(file));
#else
error_message.append(file);
#endif // UNICODE
error_message.append(TEXT("\r\nLine:\t"));
error_message.append(std::to_string(line));
error_message.append(TEXT("\r\nError:\t"));
// If HRESULT is omited or S_OK
// format last error code message
if (hr == S_OK)
{
LPVOID lpBuff = nullptr;
DWORD dwChars = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
nullptr,
GetLastError(),
0,
reinterpret_cast<LPTSTR>(&lpBuff),
0,
nullptr);
// If the function succeeds, the return value is
// the number of TCHARs stored in the output buffer
if (dwChars)
{
error_message.append(reinterpret_cast<LPCTSTR>(lpBuff));
}
else // If the function fails, the return value is zero
{
error_message.append(TEXT("Unknown Error\t"));
error_message.append(to_string(GetLastError()));
}
// Free the buffer allocated by FormatMessage
LocalFree(lpBuff);
}
else // Format com error code into a message
{
_com_error err(hr);
error_message.append(err.ErrorMessage());
}
// Play the sound and show error message
MessageBox(hWnd,
error_message.c_str(),
error_type.c_str(), MB_OK | MB_ICONERROR);
}
The function is called slight differently for system errors:
ShowError(nullptr, __FILENAME__, __LINE__); // type hresult if needed
edit:
I copied the code from my project, and currently std::to_string where mentioned works only for ANSI version, you need to modify ShowError function to conditionaly use std::to_wstring for unicode.
Also string inside ShowError function is ANSI string, you can conditionally use wstring or define a macro for string if you wan't like this:
#ifdef UNICODE
typedef std::wstring string;
#else
typedef std::string string;
#endif // UNICODE
also for to_string if you wish:
// conditionaly use string or wide string
#ifdef UNICODE
#define to_string std::to_wstring
#else
#define to_string std::to_string
#endif // UNICODE
You can also implement enum class code types and pass them to exception class as second or 3rd additional argument and implement showing the custom error code if you wish to avoid typing error message for every separate function call.
Also note that ShowError function can be used for std errors inside catch statement, where you simply pass std error object like this for example:
try
{
// example, or do some heavy memory allocation here to throw
throw std::bad_alloc;
}
catch(std::bad_alloc& err);
{
ShowError(nullptr, err, __FILENAME__, __LINE__);
}
This approach can be extended to modify the function to also format NTSTATUS messages
For complete list of possible error messages in Win32 see this.
For additional information on functions used in above code see following link:
FormatMessage function
GetLastError function
Some of the code has been copied from this site ex:
Convert to wstring
Show only file name
Format COM Error code
Lets start with the headers.
#include <system_error> // For std::error_code
#include <exception> // For std::exception
#include <iostream> // To print example output
Often you are stuck with some macros (could be constexpr int's)
#define FOO_ERROR1 1
#define FOO_ERROR2 2
You should be working in a namespace, lets use n here.
namespace n {
Step 1. define an enum type (you might already HAVE an enum, which is good).
enum class Error {
XE_FOO_ERROR1 = FOO_ERROR1, // XE_ is a random prefix to distinguish it from the macro FOO_ERROR1.
XE_FOO_ERROR2 = FOO_ERROR2
};
Step 2. define the following functions that will be found through ADL (Argument-dependent lookup). This is why the namespace (n) should be very specific for this error type. As an example, the namespace that I use for errors of my xcb system is xcb::errors::org::freedesktop::xcb. Just n is probably too short, and we should be using foo anyway, but I think it is less confusing for the example to use different names.
std::string to_string(Error code); // You probably want those enums to be convertible to strings with `to_string`.
std::ostream& operator<<(std::ostream& os, Error code); // In order to print the enum's to an ostream.
inline char const* get_domain(Error) { return "foo:org.freedesktop.foo.Error"; } // An error domain string.
std::error_code make_error_code(Error code) noexcept; // Converting the Error to a std::error_code.
} // namespace n
Step 3. register n::Error as valid error code.
namespace std {
template<> struct is_error_code_enum<n::Error> : true_type { };
} // namespace std
And yes, it is legal to define this in namespace std. It is what is intended by the standard.
Next, lets define the above functions in our .cpp file:
namespace n {
// Implement to_string
std::string to_string(Error code)
{
#if 0
switch(code)
{
case Error::XE_FOO_ERROR1:
return "FOO_ERROR1";
case Error::XE_FOO_ERROR2:
return "FOO_ERROR2";
}
return "Never reached";
#else
// Or if you use magic_enum (https://github.com/Neargye/magic_enum), we can use the simple:
auto sv = magic_enum::enum_name(code);
sv.remove_prefix(3); // Remove "XE_" from the name.
return std::string{sv};
#endif
}
// Implement operator<<
std::ostream& operator<<(std::ostream& os, Error code)
{
return os << to_string(code);
}
Before we can define make_error_code we first need to define the error category (this is still in that same .cpp file!):
namespace {
struct FooErrorCategory : std::error_category
{
char const* name() const noexcept override;
std::string message(int ev) const override;
};
char const* FooErrorCategory::name() const noexcept
{
return "n::Error"; // Can be anything you want.
}
std::string FooErrorCategory::message(int ev) const
{
auto error = static_cast<Error>(ev);
return to_string(error);
}
FooErrorCategory const theFooErrorCategory { };
} // namespace
And now we can define make_error_code.
std::error_code make_error_code(Error code) noexcept
{
return std::error_code(static_cast<int>(code), theFooErrorCategory);
}
} // namespace n
In order to support exceptions we need some exception class that supports std::error_code. This is a general class thus, not something specific to one n::Error enum. Personally I use a specialized system that you can find here, but lets just code something minimal for this example (this is the header file):
namespace errors {
struct ErrorCode : public std::exception
{
std::error_code m_ec;
char const* m_text;
ErrorCode(std::error_code ec, char const* text) : m_ec(ec), m_text(text) {}
void print_on(std::ostream& os) const;
};
inline std::ostream& operator<<(std::ostream& os, ErrorCode const& ec)
{
ec.print_on(os);
return os;
}
} // namespace errors
And the .cpp file
namespace errors {
void ErrorCode::print_on(std::ostream& os) const
{
os << m_text << ": " << m_ec.message() << " [" << m_ec << "]";
}
} // namespace errors
Finally, here is some code to test the above.
You can also find this complete example on wandbox.
int main()
{
// A test enum error code.
n::Error error = n::Error::XE_FOO_ERROR2;
// We want to be able to use to_string.
using std::to_string; // Fall back (that we are not using here).
std::cout << to_string(error) << std::endl;
// We want to be able to print an Error.
std::cout << error << std::endl;
// We want to be able to convert the error to a std::error_code.
std::error_code ec = error;
// We want to be able to print that error_code.
std::cout << ec << " [" << ec.message() << "]\n";
// You can convert a macro (or int) value to the enum type.
n::Error e1 = static_cast<n::Error>(FOO_ERROR1);
// We want to be able to throw 'Error' values.
try
{
throw errors::ErrorCode(e1, "Some text");
}
catch (errors::ErrorCode const& error)
{
std::cout << "Caught: " << error << std::endl;
}
}
Which outputs
FOO_ERROR2
FOO_ERROR2
n::Error:2 [FOO_ERROR2]
Caught: Some text: FOO_ERROR1 [n::Error:1]
Related
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
i am making a program and decided to make my own exceptions so i wrote the following header-only file:
#ifndef ARGUMENT_EXCEPTIONS
#define ARGUMENT_EXCEPTIONS
#include <exception>
namespace AAerr {
class ArgumentException : public std::exception {
private:
const char* msg;
static constexpr char* funcName = (char*)"ArgumentException";
public:
ArgumentException(const char* msg_) {
msg = msg_;
}
const char* getMessage() {
return msg;
}
virtual const char* what() const throw() {
return funcName;
}
};
class WrongReturnType : public ArgumentException {
private:
const char* msg = "Wrong Type";
char requestedType;
char ofType;
static constexpr char* funcName = (char*)"WrongReturnType";
public:
WrongReturnType(const char requested, const char is) : ArgumentException(msg) {
requestedType = requested;
ofType = is;
}
char get_requested_type() {
return requestedType;
}
char get_of_type() {
return ofType;
}
virtual const char* what() {
return funcName;
}
};
}
#endif // ARGUMENT_EXCEPTIONS
and when i tried to throw and catch one of them in my program it never caught anything:
#include "exception_header_file.hpp" // the name of the header file
#include <iostream>
int main() {
try {
throw AAerr::ArgumentException("TEST");
} catch (AAerr::ArgumentException& exc) {
std::cout << "CAUGHT EXCEPTION" << std::endl; // never executed
}
}
and then i made a second file (just to figure out where the problem is):
#include <iostream>
#include <exception>
namespace AAerr {
class Exc : public std::exception {
private:
const char* msg;
static constexpr char* funcName = (char*)"TEST FUNC";
public:
Exc(const char* msg_) {
msg = msg_;
}
const char* getMessage() {
return msg;
}
virtual const char* what() {
return funcName;
}
};
class Exc2 : public Exc {
private:
int line;
static constexpr char* funcName = (char*)"FUNCTION";
public:
Exc2(const char* msg_, int line_) : Exc(msg_) {line = line_;}
virtual const char* what() {
return funcName;
}
};
};
int main() {
try {
throw Exc2("TEST", 5);
} catch (AAerr::Exc& exc) {
std::cout << "CAUGHT EXCEPTION" << std::endl; // works fine
}
}
can someone help me find where the problem is?? i can't find any difference between these 2.
compiler: g++(gcc)
platform: ubuntu(linux)
EDIT:
i managed to fix the problem.
it was on the on the linker(ld).
i changed my Makefile to build the whole project instead of making and then linking everything.
what i mean is, i did:
build:
g++ program_part1.cpp program_part2.cpp program_part3.cpp -o example.elf
instead of:
build: part1.o part2.o part3.o
g++ part1.o part2.o part3.o -o output.elf
part1.o: program_part1.cpp program_part1.hpp
g++ -c program_part1.cpp -o part1.o
...
...
I suspect that problem is in "main program", see OP comment in other answer:
yeah i tried and it's the same thing. it doesn't work on my main program, but works fine on the testing program i have – user898238409
If "main program" is using dynamic libraries and exception are defined as "header only", this can lead to situation that RTTI (Run Time Type Information) is generated for this class multiple times. One for each include for dynamic library and main executable where this header file is included.
For example this can go like this:
dynamic library creates instance of class AAerr::ArgumentException using RTTI generated for that library when exception and throws this as exception.
main executable is trying catch that exception using RTTI which was build for that executable.
Since those two RTTI do not match to each other catch statement can't notice that expected exception is thrown.
Proper fix for that is to drop "header only" exceptions so all virtual methods should be defined in cpp file. This will lead to well defined association with specific library. As a result only one RTTI will be present for ArgumentException and WrongReturnType.
Have you tried catching std::exception instead of the exception class?
Like this:
try{
throw AAerr::Argument exception("Test");
} catch (std::exception exc) {
std::cout << "HEY" << std::endl;
}
This is a very strange issue. I have two classes: a custom console class (CConsole) and a test class (CHashTableTest) which I've made to play around with maps and unordered_maps to learn how they work.
In my console class I have a public static member variable of CConsole that exposes a static console object to the rest of the project so that I can write to this console when ever I want. This works fine for all of my classes including the test class but only when that test classes uses map and not unordered_map!
The error that I receive is:
error LNK2001: unresolved external symbol "public static class CConsole CConsole:output" (?output#CConsole##2V1#A)
It comes from the class that calls the methods on the test class and not the test class itself but nothing strange is happening in the calling class, it simply instantiates the CHashTableTest object (passing in a CConsole object) and calls Add and Get on it. It is placed in a separate project but this isn't a problem when I use map as the static member variable has been made external using _declspec(ddlexport).
The solution is setup in Visual Studio 2012, the CConsole and CHashTableTest classes are in a DLL project which is an external reference of a Unit Test project where the calling code exists.
The CConsole and CHashTableTest files:
Console.h
#ifndef _CONSOLE_H_
#define _CONSOLE_H_
#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#include <fstream>
#include <string>
#include <ctime>
#include <time.h>
// Defines a set of output modes for the CConsole class. This will affect what output the console will target.
// The default is COUT.
enum _declspec(dllexport) EConsoleMode
{
// Output sent to a CConsole object will be printed using cout.
COUT,
// Output sent to a CConsole object will be printed to a file.
OUTPUT_FILE,
// Output sent to a CConsole object will be printed using OutputDebugString.
OUTPUT_DEBUG_STRING,
// Output sent to a CConsole object will be printed using Debug::WriteLine.
DEBUG_WRITE_LINE,
// Output sent to a CConsole object will be printed using Console::WriteLine.
CONSOLE_WRITE_LINE,
// Output sent to a CConsole object will not be printed.
NO_OUTPUT,
};
// An output wrapper class that allows logging and redirecting of log and debugging messages to different
// output targets.
class _declspec(dllexport) CConsole
{
public:
static CConsole output;
// Constructs a CConsole object with a specific console mode, default is COUT.
CConsole(EConsoleMode mode = COUT, const char* filePath = "C:/output.txt");
~CConsole(void);
// Gets the mode of this CConsole object.
EConsoleMode GetMode();
const char* GetFilePath();
// Sets the output file path of this CConsole object.
void SetFilePath(const char* filePath);
void TimeStamp();
// Logs a message with this specific CConsole object. An indirect call to an operator overload of
// CConsole << Type
template <typename T> void Log(const T &message);
protected:
// The mode of this CConsole object.
EConsoleMode m_mode;
// The file path of the output file for this CConsole object.
const char* m_filePath;
};
// Operator overload of CConsole << Type, queries the mode of the given CConsole object and
// selects the appropriate output method associated with the mode.
template <typename T>
CConsole operator<< (CConsole console, const T &input)
{
switch(console.GetMode())
{
case COUT:
{
std::cout << input << "\n";
break;
}
case OUTPUT_FILE:
{
ofstream fout;
fout.open (console.GetFilePath(), ios::app);
fout << input;
fout.close();
break;
}
#if ON_WINDOWS
case OUTPUT_DEBUG_STRING:
{
OutputDebugString(input);
break;
}
case DEBUG_WRITE_LINE:
{
Debug::WriteLine(input);
break;
}
case CONSOLE_WRITE_LINE:
{
Console::WriteLine(input);
break;
}
#endif
case NO_OUTPUT:
{
break;
}
default:
{
std::cout << input;
break;
}
}
return console;
}
// Logs a message by calling the operator overload of << on this CConsole object with the message
// parameter.
template <typename T>
void CConsole::Log(const T &message)
{
this << message;
}
#endif
Console.cpp
#include "Console.h"
CConsole CConsole::output = *new CConsole(OUTPUT_FILE, "C:/LocalProjects/---/output.txt"); // Known memory leak here, discussed in comments
// Constructs a CConsole object by assigning the mode parameter to the
// m_mode member variable.
CConsole::CConsole(EConsoleMode mode, const char* filePath)
{
m_mode = mode;
m_filePath = filePath;
TimeStamp();
}
CConsole::~CConsole(void)
{
//Log("\n\n");
}
// Returns the current mode of this CConsole object.
EConsoleMode CConsole::GetMode()
{
return m_mode;
}
const char* CConsole::GetFilePath()
{
return m_filePath;
}
void CConsole::TimeStamp()
{
if(m_mode == OUTPUT_FILE)
{
std::ofstream file;
file.open (m_filePath, std::ios::app); //
std::time_t currentTime = time(nullptr);
file << "Console started: " << std::asctime(std::localtime(¤tTime));
file.close();
}
}
void CConsole::SetFilePath(const char* filePath)
{
m_filePath = filePath;
}
HashTableTest.h
#ifndef _HASH_TABLE_TEST_H_
#define _HASH_TABLE_TEST_H_
#include <unordered_map>
#include <map>
#include <vector>
#include "Debuggable.h"
#include "Console.h"
using namespace std;
//template class __declspec(dllexport) unordered_map<int, double>;
struct Hasher
{
public:
size_t operator() (vector<int> const& key) const
{
return key[0];
}
};
struct EqualFn
{
public:
bool operator() (vector<int> const& t1, vector<int> const& t2) const
{
CConsole::output << "\t\tAdding, t1: " << t1[0] << ", t2: " << t2[0] << "\n"; // If I delete this line then no error!
return (t1[0] == t2[0]);
}
};
class __declspec(dllexport) CHashTableTest : public CDebuggable
{
public:
CHashTableTest(CConsole console = *new CConsole());
~CHashTableTest(void);
void Add(vector<int> key, bool value);
bool Get(vector<int> key);
private:
CConsole m_console;
unordered_map<vector<int>, bool, Hasher, EqualFn> m_table; // If I change this to a map then no error!
};
#endif
Just to be clear, if I change 'unordered_map' above to 'map' and remove the Hasher function object from the template argument list this will compile. If I don't I get the linker error. I haven't included HashTableTest.cpp because it contains practically nothing.
EDIT
I'm not sure if I'm using unordered_map properly here is the implementation for the CHashTableTest class:
HashTableTest.cpp
#include "HashTableTest.h"
CHashTableTest::CHashTableTest(CConsole console) : CDebuggable(console)
{
}
CHashTableTest::~CHashTableTest(void)
{
}
void CHashTableTest::Add(vector<int> key, bool value)
{
m_table[key] = value;
}
bool CHashTableTest::Get(vector<int> key)
{
return m_table[key];
}
When a DLL's generated import library is being linked with an consuming EXE (or another DLL) the declspecs of the items being brought in should be declspec(dllimport) on the receiver-side. When you're building the actual DLL those same items are made available by usage of declspec(dllexport). Your code has the latter, but not the former.
This is normally achieved by a single preprocessor macro that is defined only for your DLL project in the preprocessor section of the compiler configuration. For example in your header(s) that are declaring things exported from your DLL:
#ifndef MYDLL_HEADER_H
#ifdef MYDLL_EXPORTS
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif
class EXPORT CConsole
{
// stuff here.
};
#endif
Doing this will tell the compiler when building the DLL to export the class members, including class statics, because MYDLL_EXPORTS is defined in the preprocessor configuration flags for your DLL project.
When building a consuming project, do NOT define MYDLL_EXPORTSin the preprocessor configuration of the compiler settings. It will thusly use dllimport, rather than dllexport. That should square away the linker to know where to look for the things it needs.
Best of luck.
Is it possible to capture and print name of the function which initialized a class object? What I want is something like this:
class MyException: public std::exception
{
public:
MyException(const std::string message, const std::string caller = __func__)
: _caller(caller),
_msg(message)
{
...
}
std::string what(void); // prints messaged along with the caller
private:
...
}
In the above class MyException I want the caller to capture the function name where an object was instantiated so that user is warned of which function threw. I can always do
...
throw MyException(message, __func__);
...
while removing the default __func__. But then I am doing this trivial thing every time I instantiate an MyException object.
Is there a better way to instruct user exactly which function throws?
Thanks in advance,
Nikhil
If I wanted to provide automatic context for my exceptions I'd do it like this:
#include <iostream>
#include <stdexcept>
#include <string>
struct context_info {
explicit context_info(std::string function)
: _function { std::move(function) }
{}
// perhaps the context info will increase later to include class and instance?
friend std::string to_string(const context_info& ci)
{
return ci._function;
}
private:
std::string _function;
};
struct my_exception : std::runtime_error
{
my_exception(const std::string& message)
: std::runtime_error(message)
{}
my_exception(context_info context, const std::string& message)
: my_exception(message + " called from " + to_string(context))
{}
// everything else already taken care of for you
};
#if defined(NDEBUG)
#define THROW_WITH_CONTEXT(TYPE, ...) throw TYPE(__VA_ARGS__)
#else
#define THROW_WITH_CONTEXT(TYPE, ...) throw TYPE(context_info(__func__), __VA_ARGS__)
#endif
using namespace std;
void do_foo()
try {
THROW_WITH_CONTEXT(my_exception, "foo failure");
}
catch(const exception& e) {
cout << e.what() << endl;
}
int main() {
// your code goes here
try {
do_foo();
THROW_WITH_CONTEXT(my_exception, "error");
}
catch(const exception& e) {
cout << e.what() << endl;
}
return 0;
}
expected output:
foo failure called from do_foo
error called from main
However, neat as this is it's probably not what you should really be doing as it won't provide you with enough context in a large complex program.
have a look at the documentation for std::throw_with_nested etc. You can catch an exception at each stage of the call stack and re-throw it with additional context information. This is much more useful.
http://en.cppreference.com/w/cpp/error/throw_with_nested
So here's an interesting question, How would I make something kinda like a wrapper for cout?
I want to be able to add it into a dll so I can throw it into my programs. but the basic syntax of it should be
Mything::mesage << "I'm some text" << im_an_int << someclass << mything::endl;
or
Mything::mesageandlog << "I'm going to print to console, and to a file!" << mything::endl;
I can handle most of the internal logic but as to what I should put to even do this. kinda stumped.
Possibly make a static stream member in my class called message, then have an event fire when its written too that runs it through a method?
Idk, I looked around and found something sortA similar, but as for throwing it into a dll I'm at a loss. (How to write a function wrapper for cout that allows for expressive syntax?)
because this requires me to use extern and a variable, but how would I make it static so I can just straight call it without creating a variable?
Bit of clarification, something like this:
mydll.h
#include <iostream>
namespace mynamespace {
extern struct LogMessage{};
template <typename T>
LogMessage& operator<< (LogMessage &s, const T &x) {
SetStdHandle(STD_OUTPUT_HANDLE, GetStdHandle(STD_OUTPUT_HANDLE));
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_BLUE);
std::cout << "[IF] ";
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_WHITE);
//LogTimestamp(); --ill impliment this.
std::cout << x << endl;
//writeStreamToLogfile(s); --and ill handle this.
return s;
}
}
driverprogram.h
#include <mydll.h>
#include <iostream>
int _tmain(int argc, _TCHAR* argv[])
{
mynamespace::LogMessage << "Something: << std::endl;
}
expected output:
"[IF] [00:00:00] Something
You can create a struct, that has a << operator
struct OutputThing
{
template< class T >
OutputThing &operator<<( T val )
{
std::cout<<val;
return *this;
}
};
Now whenever you want to log, you will have to instance the object.
OutputThing()<<"x ="<<x;
If you want to avoid the repeated construction and destruction of the object, you can make it a singleton.
struct OutputThingSingleton
{
static OutputThingSingleton& GetThing()
{
static OutputThingSingleton OutputThing;
return OutputThing;
}
template< class T >
OutputThingSingleton &operator<<( T val )
{
std::cout<<val;
return *this;
}
private:
OutputThingSingleton()
{};
};
So the call now looks like
OutputThingSingleton::GetThing()<<"x ="<<x;
Which you could shorten using a macro.
This will work across multiple dlls, however depending on how it is used you can have multiple instances of the singleton existing. This would work fine as long as you don't want to maintain any state in your singleton. If you do need to ensure a single instance, you can compile it in its own dll. Any other binary that uses this dll will share the single instance 'owned' by the dll.
First of all, just to give fair warning, I'm pretty sure this won't work in a DLL. You want to put it into a header (as it's shown here).
Second, it's probably a little more elaborate than you were considering. In particular, it defines a multi-output stream class that works like any other stream. Essentially any normal overload of operator<< should work fine with it.
Unlike a normal stream operator, however, the output goes to multiple streams, and each line of output (on all the streams) is preceded by a prefix (currently set to the value "[FIX]", but it just uses the content of a string, so whatever you put in that string should work. A more polished/finished implementation would probably allow you to set the prefix with something like a manipulator, but this (currently) doesn't support that.
Finally, it does some variadic template trickery, so you can specify the output files as either file names or existing ostream objects, or a combination thereof (e.g., see demo main at end).
First, the header:
#ifndef LOGGER_H_INC_
#define LOGGER_H_INC_
#include <iostream>
#include <streambuf>
#include <vector>
#include <fstream>
class logger: public std::streambuf {
public:
logger(std::streambuf* s): sbuf(s) {}
~logger() { overflow('\n'); }
private:
typedef std::basic_string<char_type> string;
int_type overflow(int_type c) {
if (traits_type::eq_int_type(traits_type::eof(), c))
return traits_type::not_eof(c);
switch (c) {
case '\n':
case '\r': {
prefix = "[FIX]";
buffer += c;
if (buffer.size() > 1)
sbuf->sputn(prefix.c_str(), prefix.size());
int_type rc = sbuf->sputn(buffer.c_str(), buffer.size());
buffer.clear();
return rc;
}
default:
buffer += c;
return c;
}
}
std::string prefix;
std::streambuf* sbuf;
string buffer;
};
namespace multi {
class buf : public std::streambuf {
std::vector<std::streambuf *> buffers;
public:
typedef std::char_traits<char> traits_type;
typedef traits_type::int_type int_type;
buf() {}
void attach(std::streambuf *s) { buffers.push_back(s); }
void attach(std::ofstream &s) { buffers.push_back(s.rdbuf()); }
int_type overflow(int_type c) {
bool eof = false;
for (std::streambuf *buf : buffers)
eof |= (buf->sputc(c) == traits_type::eof());
return eof ? traits_type::eof() : c;
}
};
class logstream : public std::ostream {
std::vector<std::ofstream *> streams;
buf outputs;
logger log;
void attach(std::ostream &s) { outputs.attach(s.rdbuf()); }
void attach(char const *name) {
std::ofstream *s = new std::ofstream(name);
streams.push_back(s);
outputs.attach(s->rdbuf());
}
template <typename T, typename...pack>
void attach(T &t, pack&...p) {
attach(t);
attach(p...);
}
public:
template <typename...pack>
logstream(pack&...p) : log(&outputs), std::ostream(&log) { attach(p...); }
~logstream() {
for (auto d : streams) {
d->close();
// Bug: crashes with g++ if delete is allowed to execute.
//delete d;
}
}
};
}
#endif
Then the demo of how to use it:
#include "logger"
int main(){
multi::logstream l(std::cout, "c:/path/log.txt");
l << "This is a prefixed string\n";
}
Obviously the header is fairly large, but the code to use it seems (at least to me) about as simple as you can hope for -- create an object, specifying where you want the output to go, just a normal stream -- except that you can specify more than one. Then write to it like you would to any other stream, and the output goes to all of the specified outputs, with each line preceded by the specified prefix.