There are three ways I know of to create an immutable object.
Method 1: internally immutable class members, internally and externally unmodifiable.
#ifndef INTERNALLY_IMMUTABLE_HPP
#define INTERNALLY_IMMUTABLE_HPP
#include <string>
class internally_immutable
{
public:
internally_immutable(const std::string & str)
: str_(str)
{
}
std::string get_str() const
{
return str_;
}
private:
const std::string str_;
};
#endif
Method 2: externally immutable class members, externally unmodifiable.
#ifndef EXTERNALLY_IMMUTABLE_HPP
#define EXTERNALLY_IMMUTABLE_HPP
#include <string>
#include <vector>
class externally_immutable
{
public:
externally_immutable(const std::string & str)
: str_(str)
{
}
std::string get_str() const
{
return str_;
}
private:
std::string str_;
};
#endif
Method 3: type immutable, partially externally unmodifiable, as someone could bypass your typedef.
#ifndef TYPED_IMMUTABLE_HPP
#define TYPED_IMMUTABLE_HPP
#include <string>
#include <vector>
typedef const typed_mutable typed_immutable;
class typed_mutable
{
public:
typed_mutable(const std::string & str)
: str_(str),
vec_()
{
}
void set_str(const std::string & str)
{
str_ = str;
}
std::string get_str() const
{
return str_;
}
private:
std::string str_;
};
#endif
What are the pros and cons of each immutable type? Compiler optimizations, impediments, usage of each type... Are there other ways of creating immutable objects in C++? What is the recommended, or most common way to create these immutable classes in C++?
you can make the immutable class when will you make your constructor private in this case your derived class can't access base class.
like as .
class externally_immutable
{
private :
externally_immutable(const std::string & str)
: str_(str){}
// to do
const string str_;
};
Related
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.
I have gender class with Male and Female as my parametric types of class
I am using following hierarchy:
#ifndef __GENDER_H
#define __GENDER_H
#include <string>
using namespace std;
// Forward declaration of templatized class
template<typename T>
class GenderTypes; // Generic Gender type to generate specific genders
// Generic gender type
class Gender { // Abstract Base Class
const string& name_; // Name of the Gender
struct MaleType {};
struct FemaleType {};
protected:
Gender(const string& name) : name_(name) {}
virtual ~Gender() { }
public:
const string& GetName() const { return name_; }
bool IsMale(const Gender&); // Checking and matching gender
// Enumerated types - the target sub-types
typedef GenderTypes<MaleType> Male;
typedef GenderTypes<FemaleType> Female;
};
// Specific gender types
template<typename T>
class GenderTypes : public Gender {
static const string sName;
GenderTypes(const string& name = GenderTypes<T>::sName) : Gender(name) { }
~GenderTypes() { }
public:
// Singleton object - placeholder for the respective type
static const GenderTypes<T>& Type() {
static const GenderTypes<T> theObject; // May be non-const for changeable behavior
return theObject;
}
};
inline bool Gender::IsMale(const Gender& g) { return &g == &Gender::Male::Type(); }
#endif
And declaring the static member name_ as follows:
#include <string>
using namespace std;
#include "../inc/gender.h"
// Names defined as static constants
const string Gender::Male::sName = "Male";
const string Gender::Female::sName = "Female";
This kind of hierarchy is fine . then why compiler gives this error:
gender.cpp:5:14: error: specializing member ‘GenderTypes<Gender::MaleType>::sName’ requires ‘template<>’ syntax
5 | const string Gender::Male::sName = "Male";
how should i initialize this static datas?
I am using VS CODE editor and Ubuntu 20.04
With this small change in your .cpp it compiles (and works) fine on my machine :
template<>
const string Gender::Male::sName = "Male";
template<>
const string Gender::Female::sName = "Female";
And indeed in your header you should use
#ifndef GENDER_H
#define GENDER_H
[...]
#endif
OR
#pragma once
[...]
I'm trying to write a class, that stores configuration items with their name, a description and their types.
OptionItem.h:
#include <typeinfo>
#include <string>
class OptionItem {
public:
OptionItem(std::string name, std::string text, type_info type);
std::string name() const;
std::string text() const;
type_info OptionItem::type() const;
private:
std::string _name, _text;
type_info _type;
};
OptionItem.cpp:
OptionItem::OptionItem(std::string name, std::string text, type_info type) :
_name(name), _text(text), _type(type) {};
std::string OptionItem::name() const { return _name; }
std::string OptionItem::text() const { return _text; }
type_info OptionItem::type() const { return _type; }
I have a second class that holds the different options:
Opt.h:
#include "OptionItem.h"
struct Opt {
static const OptionItem opt1, opt2;
};
Opt.cpp:
#include "myOwnClass.h"
const OptionItem Opt::opt1= OptionItem("Option 1", "text1", typeid(std::string));
const OptionItem Opt::opt2= OptionItem("Option 2", "text2", typeid(myOwnClass));
A third class that held the actual settings of my program would then look like this:
Settings.h
#include "OptionItem"
#include <any>
#include <map>
#include <string>
class Settings {
public:
void setOption(OptionItem option, std::any value);
template<class T> T& getOption(const std::string &option) const;
private:
std::map<std::string, std::any> _options;
}
Settings.cpp
void Settings::setOption(OptionItem option, std::any value) {
_options.emplace(option.name(), value);
}
T& Settings::getOption(const std::string &option) const {
if (_options.find(option) == _options.end()) {
throw(std::runtime_error("No such option \"" + option + "\"
}
else {
return std::any_cast<T>(_options[option]);
}
}
A call to Settings::getOption might look like this:
getOption<Opt::opt1.type()>(Opt::opt1.name())
I know this has its problems (which is, why I'm asking :-) ) the first one being that std::type_info doesn't seem to be copyable. (the compiler(VIsualCPP) tells me 'type_info::type_info(const type_info &)': attempting to reference a deleted function). The other problem, of which I am sure there are plenty I don't know about since compilation stops with the aforementioned error.
Do you have any suggestions how I could make this work?
Static1.hpp
#include <string>
class Static1
{
public:
static const std::string my_string;
};
Static1.cpp
#include "Static1.hpp"
const std::string Static1::my_string = "aaa";
Static2.hpp
#include <string>
class Static2
{
public:
static const std::string my_string;
};
Static2.cpp
#include "Static2.hpp"
const std::string Static2::my_string = Static1::my_string;
main.cpp
#include "Static2.hpp"
#include <iostream>
int main(argc int, char** argv)
{
cout << to_string(Static2::my_string == "aaa") << endl;
return 0;
}
If I put add_executable(printMyString main.cpp Static2.cpp Static1.cpp) in my CMakeLists.txt, I get
0
while add_executable(printMyString main.cpp Static2.cpp Static1.cpp) gives me the expected behavior of
1
To make my code easier to maintain (so that I don't need to keep track of the order I list my source files), is there any way I can ensure that I get the behavior where Static2::my_string == "aaa"?
You are experiencing effects of a static initialization order fiasco.
The usual work-around is to substitute your static variables with functions that have a static variable in the scope, initialize, and return it.
Here is how it could be done for your example: Live Example (order1)
Live Example (order2)
class Static1
{
public:
static std::string my_string();
};
...
std::string Static1::my_string()
{
static const std::string my_string = "aaa";
return my_string;
}
...
class Static2
{
public:
static std::string my_string();
};
...
std::string Static2::my_string()
{
static const std::string my_string = Static1::my_string();
return my_string;
}
...
std::cout << std::to_string(Static2::my_string() == "aaa") << std::endl;
I'm trying to implement a C++ factory class that also perform the self registration of some derived classes. My implementation is based on the library:
http://arcticinteractive.com/2008/10/06/boost-centric-factory-pattern-implementation/
that is based on the Boost library. Just to give you a quick overview of this library, here is a simple self-explained (I hope) example:
struct foo { virtual ~foo() {} };
struct bar : foo { bar(int i) { std::cout << "bar() " << i << "\n"; } };
struct baz : foo { baz(int i) { std::cout << "baz() " << i << "\n"; } };
...
typedef factory< foo*(int) > myfactory_t;
myfactory_t f;
// Register a default (operator new) creator function
// for an implementation type
register_new_ptr<bar>(f, "bar");
register_new_ptr<baz>(f, "baz");
// Create objects through the factory
foo* fooimpl1 = f["bar"](1234);
foo* fooimpl2 = f["baz"](4321);
What I'm trying to do is to delegate each class to self-register them self to the factory using a static method. Here is the code:
animal.h
#pragma once
#include <cstring>
#include <iostream>
#include "factory.hpp"
#include "abstract_factory.hpp"
class zoo;
using namespace std;
using namespace boost::factory;
class animal{
virtual const std::string do_sound() const = 0;
std::string name_;
int age_;
zoo* myZoo_;
public:
animal(const std::string& name, int age, zoo* myZoo) : name_(name), age_(age), myZoo_(myZoo)
{}
virtual ~animal() {}
const std::string sound() const
{
return do_sound();
}
const std::string& name() const { return name_; }
const int age() const { return age_; }
};
template <class T>
struct animalFactory{
typedef factory< animal*(std::string&, int, zoo*) > myfactory_t;
static const myfactory_t* f;
static bool registerAnimal(const std::string& animalname){
return register_new_ptr<T>(&f, animalname);
};
};
When I try to register a class like:
crocodile.cpp
#include "crocodile.h"
bool r = animalFactory<crocodile>::registerAnimal("crocodile");
I get an error from visual studio 2012 that is:
animal.h(41): error C2893: Failed to specialize function template 'bool boost::factory::register_new_ptr(Factory &,Factory::id_param_type)'
Could someone help me to understand what is going on here? Thanks a lot!