A
Adrian
I have some class to read and store options from a config file.
I have a design which works for the basic types. What I want to add is an array
like type. I am not sure how to define the Field Class specific template. What I
tried doesnt work.
I have included a cut down version of the code.
TIA
------example.conf
LogFile /usr/home/interlex/www/logs/access_log
OutputDir /home/interlex/www/htdocs/weblogs
PageType htm*
PageType cgi
PageType php
test_int 3467
test_float 3.143
test_double 4.54675745
test_bool true
test_bool2 false
----------code
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <stdexcept>
#include <cstdlib>
#include <locale>
#include <vector>
class FieldBase
{
public:
virtual ~FieldBase() {};
virtual void Set(const std::string &s)=0;
virtual FieldBase &operator=(const std::string &s)=0;
private:
};
template<class T>
class Field: public FieldBase
{
public:
void Set(const std::string &s);
virtual Field &operator=(const std::string &s);
T Get() const { return T(data); };
~Field() {};
private:
T data;
};
template<>
class Field<int> : public FieldBase
{
public:
void Set(const std::string &s)
{
data=std::atoi(s.c_str());
};
Field &operator=(const std::string &s)
{
Set(s);
return *this;
};
int Get() const { return int(data); };
~Field() {};
private:
int data;
};
template<>
class Field<std::string>: public FieldBase
{
public:
void Set(const std::string &s)
{
data=s;
};
Field &operator=(const std::string &s)
{
Set(s);
return *this;
};
std::string Get() const { return std::string(data); };
~Field() {};
private:
std::string data;
};
template<>
class Field<float>: public FieldBase
{
public:
void Set(const std::string &s)
{
data=std::atof(s.c_str());
};
Field &operator=(const std::string &s)
{
Set(s);
return *this;
};
float Get() const { return float(data); };
~Field() {};
private:
float data;
};
template<>
class Field<double>: public FieldBase
{
public:
void Set(const std::string &s)
{
data=std::atof(s.c_str());
};
Field &operator=(const std::string &s)
{
Set(s);
return *this;
};
double Get() const { return double(data); };
~Field() {};
private:
double data;
};
template<>
class Field<bool>: public FieldBase
{
public:
void Set(const std::string &s)
{
std::string temp=s;
std::string::iterator i=temp.begin();
while(i!=temp.end())
{
(*i)=std::toupper(*i);
++i;
}
data=temp=="TRUE";
};
Field &operator=(const std::string &s)
{
Set(s);
return *this;
};
bool Get() const { return bool(data); };
~Field() {};
private:
bool data;
};
#if 0
template<>
class Field<std::vector<std::string> >: public FieldBase
{
public:
void Set(const std::string &s)
{
data.push_back(s);
}
Field &operator=(const std::string &s)
{
Set(s);
return *this;
}
std::vector<std::string> Get() const {return data;};
~Field() {};
private:
std::vector<std::string> data;
}
#endif
class Config
{
public:
virtual ~Config();
void parse(void);
template<class T>
T Get(const std::string &f) const
{
Fields_t::const_iterator i=fields_.find(f);
if(i!=fields_.end())
{
try
{
const Field<T> &ptr=dynamic_cast<Field<T> &>(*i->second);
return ptr.Get();
}
catch(...)
{
throw;
}
}
throw std::runtime_error("Config field " + f + " does not exist");
}
template<class T>
void AddField(const std::string &fieldname, bool required=true)
{
fields_[fieldname]=new Field<T>();
}
protected:
Config(const std::string &file);
private:
typedef std::map<std::string, FieldBase *> Fields_t;
void trim_(std::string &l);
Fields_t fields_;
std::string filename_;
static const char comment_char_='#';
};
class ExampleConfig : public Config
{
public:
ExampleConfig(const std::string &file);
private:
};
Config::Config(const std::string &file)
: filename_(file)
{
}
Config::~Config()
{
for(Fields_t::iterator i=fields_.begin(); i!=fields_.end(); i++)
{
delete (i->second);
}
}
void Config:
arse(void)
{
std::ifstream in;
std::string keyword;
std::string value;
std::string line;
in.open(filename_.c_str());
if(!in.is_open())
{
throw std::runtime_error("Could not open file: " + filename_);
}
while(getline(in, line))
{
trim_(line);
if(line[0]==comment_char_ || line.empty())
{
continue;
}
keyword=line.substr(0, line.find_first_of(" \t"));
line.erase(0, keyword.length());
value=line.substr(line.find_first_not_of(" \t"));
FieldBase *ptr=fields_[keyword];
if(ptr)
{
ptr->Set(value);
}
}
}
void Config::trim_(std::string &l)
{
const char *white_space=" \t";
l.erase(0, l.find_first_not_of(white_space));
l.erase(l.find_last_not_of(white_space)+1);
}
ExampleConfig::ExampleConfig(const std::string &file)
:Config(file)
{
AddField<std::string>("OutputDir");
AddField<std::string>("LogFile");
AddField<std::string>("PageType");
// I want to be able to do something like below
// AddField<std::vector<std::string> >("PageType");
AddField<float>("test_float");
AddField<double>("test_double");
AddField<bool>("test_bool");
AddField<bool>("test_bool2");
AddField<int>("test_int");
parse();
}
int main(int argc, char *argv[])
{
try
{
ExampleConfig config("example.conf");
std::cout << config.Get<std::string>("LogFile") << std::endl;
std::cout << config.Get<std::string>("OutputDir") << std::endl;
std::cout << config.Get<std::string>("PageType") << std::endl;
// I want to be able to do something like below
// std::cout << config.Get<std::vector<std::string> >("PageType")[0] << std::endl;
std::cout << config.Get<int>("test_int") << std::endl;
std::cout << config.Get<float>("test_float") << std::endl;
std::cout << config.Get<double>("test_double") << std::endl;
std::cout << config.Get<bool>("test_bool") << std::endl;
std::cout << config.Get<bool>("test_bool2") << std::endl;
std::cout << config.Get<std::string>("HideURL") << std::endl;
}
catch(const std::exception &e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
I have a design which works for the basic types. What I want to add is an array
like type. I am not sure how to define the Field Class specific template. What I
tried doesnt work.
I have included a cut down version of the code.
TIA
------example.conf
LogFile /usr/home/interlex/www/logs/access_log
OutputDir /home/interlex/www/htdocs/weblogs
PageType htm*
PageType cgi
PageType php
test_int 3467
test_float 3.143
test_double 4.54675745
test_bool true
test_bool2 false
----------code
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <stdexcept>
#include <cstdlib>
#include <locale>
#include <vector>
class FieldBase
{
public:
virtual ~FieldBase() {};
virtual void Set(const std::string &s)=0;
virtual FieldBase &operator=(const std::string &s)=0;
private:
};
template<class T>
class Field: public FieldBase
{
public:
void Set(const std::string &s);
virtual Field &operator=(const std::string &s);
T Get() const { return T(data); };
~Field() {};
private:
T data;
};
template<>
class Field<int> : public FieldBase
{
public:
void Set(const std::string &s)
{
data=std::atoi(s.c_str());
};
Field &operator=(const std::string &s)
{
Set(s);
return *this;
};
int Get() const { return int(data); };
~Field() {};
private:
int data;
};
template<>
class Field<std::string>: public FieldBase
{
public:
void Set(const std::string &s)
{
data=s;
};
Field &operator=(const std::string &s)
{
Set(s);
return *this;
};
std::string Get() const { return std::string(data); };
~Field() {};
private:
std::string data;
};
template<>
class Field<float>: public FieldBase
{
public:
void Set(const std::string &s)
{
data=std::atof(s.c_str());
};
Field &operator=(const std::string &s)
{
Set(s);
return *this;
};
float Get() const { return float(data); };
~Field() {};
private:
float data;
};
template<>
class Field<double>: public FieldBase
{
public:
void Set(const std::string &s)
{
data=std::atof(s.c_str());
};
Field &operator=(const std::string &s)
{
Set(s);
return *this;
};
double Get() const { return double(data); };
~Field() {};
private:
double data;
};
template<>
class Field<bool>: public FieldBase
{
public:
void Set(const std::string &s)
{
std::string temp=s;
std::string::iterator i=temp.begin();
while(i!=temp.end())
{
(*i)=std::toupper(*i);
++i;
}
data=temp=="TRUE";
};
Field &operator=(const std::string &s)
{
Set(s);
return *this;
};
bool Get() const { return bool(data); };
~Field() {};
private:
bool data;
};
#if 0
template<>
class Field<std::vector<std::string> >: public FieldBase
{
public:
void Set(const std::string &s)
{
data.push_back(s);
}
Field &operator=(const std::string &s)
{
Set(s);
return *this;
}
std::vector<std::string> Get() const {return data;};
~Field() {};
private:
std::vector<std::string> data;
}
#endif
class Config
{
public:
virtual ~Config();
void parse(void);
template<class T>
T Get(const std::string &f) const
{
Fields_t::const_iterator i=fields_.find(f);
if(i!=fields_.end())
{
try
{
const Field<T> &ptr=dynamic_cast<Field<T> &>(*i->second);
return ptr.Get();
}
catch(...)
{
throw;
}
}
throw std::runtime_error("Config field " + f + " does not exist");
}
template<class T>
void AddField(const std::string &fieldname, bool required=true)
{
fields_[fieldname]=new Field<T>();
}
protected:
Config(const std::string &file);
private:
typedef std::map<std::string, FieldBase *> Fields_t;
void trim_(std::string &l);
Fields_t fields_;
std::string filename_;
static const char comment_char_='#';
};
class ExampleConfig : public Config
{
public:
ExampleConfig(const std::string &file);
private:
};
Config::Config(const std::string &file)
: filename_(file)
{
}
Config::~Config()
{
for(Fields_t::iterator i=fields_.begin(); i!=fields_.end(); i++)
{
delete (i->second);
}
}
void Config:
{
std::ifstream in;
std::string keyword;
std::string value;
std::string line;
in.open(filename_.c_str());
if(!in.is_open())
{
throw std::runtime_error("Could not open file: " + filename_);
}
while(getline(in, line))
{
trim_(line);
if(line[0]==comment_char_ || line.empty())
{
continue;
}
keyword=line.substr(0, line.find_first_of(" \t"));
line.erase(0, keyword.length());
value=line.substr(line.find_first_not_of(" \t"));
FieldBase *ptr=fields_[keyword];
if(ptr)
{
ptr->Set(value);
}
}
}
void Config::trim_(std::string &l)
{
const char *white_space=" \t";
l.erase(0, l.find_first_not_of(white_space));
l.erase(l.find_last_not_of(white_space)+1);
}
ExampleConfig::ExampleConfig(const std::string &file)
:Config(file)
{
AddField<std::string>("OutputDir");
AddField<std::string>("LogFile");
AddField<std::string>("PageType");
// I want to be able to do something like below
// AddField<std::vector<std::string> >("PageType");
AddField<float>("test_float");
AddField<double>("test_double");
AddField<bool>("test_bool");
AddField<bool>("test_bool2");
AddField<int>("test_int");
parse();
}
int main(int argc, char *argv[])
{
try
{
ExampleConfig config("example.conf");
std::cout << config.Get<std::string>("LogFile") << std::endl;
std::cout << config.Get<std::string>("OutputDir") << std::endl;
std::cout << config.Get<std::string>("PageType") << std::endl;
// I want to be able to do something like below
// std::cout << config.Get<std::vector<std::string> >("PageType")[0] << std::endl;
std::cout << config.Get<int>("test_int") << std::endl;
std::cout << config.Get<float>("test_float") << std::endl;
std::cout << config.Get<double>("test_double") << std::endl;
std::cout << config.Get<bool>("test_bool") << std::endl;
std::cout << config.Get<bool>("test_bool2") << std::endl;
std::cout << config.Get<std::string>("HideURL") << std::endl;
}
catch(const std::exception &e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}