I am having problems with a template specialization. Below are two classes, AbstractSetting (parent) and Setting (child). (AbstractSetting is probably not important, but I am including it for context.)
The ultimate goal of this code is to create a container to hold various INI settings of different types -- string, int, enum, etc. (DataType), that can be referenced with an enum (IndexType). There may be different index enums in different contexts (main game, test suite, server, etc.).
I am trying to create a series of fromString methods that, when passed a string, return an object of DataType (one of my template parameters).
The code as presented will compile but not link.
If I uncomment the assert, it will link, but none of the specializations are called, and the assert trips on every call to fromString, regardless of the parameters.
How can I make this work?
Note that U32, S32, etc. are types of ints.
template <class IndexType>
class AbstractSetting
{
private:
IndexType mName; // Value we use to look this item up
string mIniKey; // INI key
string mIniSection; // INI section
string mComment;
public:
// Constructor
AbstractSetting(IndexType name, const string &key,
const string §ion, const string &comment):
mIniKey(key),
mIniSection(section),
mComment(comment)
{
mName = name;
}
~AbstractSetting() { /* Do nothing */ } // Destructor
IndexType getName() const { return mName; }
string getKey() const { return mIniKey; }
string getSection() const { return mIniSection; }
string getComment() const { return mComment; }
virtual void setValFromString(const string &value) = 0;
virtual string getValueString() const = 0; // Returns val as str
virtual string getDefaultValueString() const = 0; // Returns def val as str
};
////////////////////////////////////////
////////////////////////////////////////
template <class DataType, class IndexType>
class Setting : public AbstractSetting<IndexType>
{
typedef AbstractSetting Parent;
private:
DataType mDefaultValue;
DataType mValue;
public:
Setting(IndexType name, const DataType &defaultValue, const string &iniKey,
const string &iniSection, const string &comment):
Parent(name, iniKey, iniSection, comment),
mDefaultValue(defaultValue),
mValue(defaultValue)
{
// Do nothing
}
~Setting() { /* Do nothing */ }
// Templated declaration
DataType fromString(const string &val) ; //{ Assert(false, "Specialize me!"); }
// Specializations
template<string *> string
fromString(const string &val) { return val; }
template<S32 *> S32
fromString(const string &val) { return atoi(val.c_str()); }
template<U32 *>
U32 fromString(const string &val) { return atoi(val.c_str()); }
template<U16 *>
U16 fromString(const string &val) { return atoi(val.c_str()); }
template<DisplayMode *>
DisplayMode fromString(const string &val) { return stringToDisplayMode(val); }
template<YesNo *>
YesNo fromString(const string &val) { return stringToYesNo(val); }
template<RelAbs *>
RelAbs fromString(const string &val) { return stringToRelAbs(val); }
template<ColorEntryMode *>
ColorEntryMode fromString(const string &val) { return stringToColorEntryMode(val); }
template<GoalZoneFlashStyle *>
GoalZoneFlashStyle fromString(const string &val) { return stringToGoalZoneFlashStyle(val); }
template<Color *>
Color fromString(const string &val) { return Color::iniValToColor(val); }
void setValue(const DataType &value) { mValue = value; }
DataType getValue() const { return mValue; }
string getValueString() const { return toString(mValue); }
string getDefaultValueString() const { return toString(mDefaultValue); }
void setValFromString(const string &value) { setValue(fromString(value)); }
};
I found a solution!
Since it seems that member functions cannot be partially specialized, I solved the problem by creating a new class that does not use the IndexType parameter.
class Evaluator
{
public:
template <class DataType> DataType fromString(const string &val);
};
In the .cpp file, I added:
// Templated default - needs to be overriden
template<class DataType> DataType
Evaluator::fromString(const string &val) {
Assert(false, "Specialize me!");
return DataType();
}
// Specializations.
// NOTE: All template specializations must be declared in the namespace scope to be
// C++ compliant. Shame on Visual Studio!
template<> string
Evaluator::fromString(const string &val) { return val; }
template<> S32
Evaluator::fromString(const string &val) { return atoi(val.c_str()); }
template<> U32
Evaluator::fromString(const string &val) { return atoi(val.c_str()); }
template<> U16
Evaluator::fromString(const string &val) { return atoi(val.c_str()); }
template<> DisplayMode
Evaluator::fromString(const string &val) { return stringToDisplayMode(val); }
template<> YesNo
Evaluator::fromString(const string &val) { return stringToYesNo(val); }
template<> RelAbs
Evaluator::fromString(const string &val) { return stringToRelAbs(val); }
template<> ColorEntryMode
Evaluator::fromString(const string &val) { return stringToColorEntryMode(val); }
template<> GoalZoneFlashStyle
Evaluator::fromString(const string &val) { return stringToGoalZoneFlashStyle(val); }
template<> Color
Evaluator::fromString(const string &val) { return Color::iniValToColor(val); }
Then, in the Setting class, I replaced the similar block of code with this call:
DataType fromString(const string &val) {
return mEvaluator.fromString<DataType>(val);
}
This seems to work nicely, and is pretty readable.
If anyone is interested, the full code will be available at the link below, after it is more fully tested and checked in.
https://code.google.com/p/bitfighter/source/browse/zap/Settings.h
Related
I have a class Column.h, a template ColumnImpl.h and a Tab.h
class Column {
public:
Column() {}
virtual ~Column() {};
virtual string getType() = 0;
};
template <typename T> class ColumnImpl : public Column {
public:
ColumnImpl() : Column() {}
void addElem(const T& elem_to_add);
protected:
vector<T> _data;
};
class Tab {
public:
Tab();
~Tab() {};
template <typename S> void addElemToCol(const string & col_name, const S & data_to_add);
private:
map<string, shared_ptr<Column>> _columns;
};
What I would like to do now, is adding elements to the template vector inside ColumnImpl.h using the method addElem, and then recalling it inside addElemToCol.
To do that I've tried (inside addElemToCol):
if (this->colIsPresent(col_name)) {
const auto & it = _columns.find(col_name);
it->second.get().
}
But here I realized I don't have the addElem method... How can I solve this issue?
EDIT 1:
This is how I'm checking the type and adding it into the vector:
int data;
data_stream >> data;
if (!data_stream.fail()) {
if (target_database.tabIsPresent(tab_name)) {
target_database.addElemToColOfTab(tab_name, col_name, data);
}
This is a Database method:
template <typename D> void addElemToColOfTab(const string & tab_name, const string & col_name, const D& data_to_add) {
const auto & it_target_tab = _tables.find(tab_name);
it_target_tab->second.addElemToCol(col_name, data_to_add);
}
This is a Tab method:
template <typename S> void addElemToCol(const string & col_name, const S & data_to_add) {
if (this->colIsPresent(col_name)) {
const auto & it = _columns.find(col_name);
switch (Type2Int(it->second->getType())) {
case OptionInt: {
ColumnImpl<int>* p_int = dynamic_cast<ColumnImpl<int> *>(it->second.get());
if (p_int != nullptr) {
p_int->addElem(data_to_add);
}
break;
}
case OptionFloat: {...}
// [...] All the other Columns
}
}
}
And every class IntColumn, FloatColumn has its own implementation:
void addElem(const int& elem_to_add) { _data.push_back(elem_to_add); } // this is for IntColumn
You are getting this error because all of the switch blocks need to compile even if only one is taken. p_int->addElem(data_to_add); can only compile when the type of data_to_add matches the corresponding ColumnImpl template type, and that's only true in one of the cases.
Since you know what S is, you don't actually need to go through any of the type-checking hoops; you can eliminate the switch altogether and just look for ColumnImpl<S>. We can also have the function return a bool indicating whether adding the element was successful.
template <typename S>
bool addElemToCol(const string & col_name, const S & data_to_add) {
const auto & it = _columns.find(col_name);
if (it != _columns.end()) {
auto ci = dynamic_cast<ColumnImpl<S> *>(it->second.get());
if (ci != nullptr) {
ci->addElem(data_to_add);
return true;
}
}
return false;
}
I have template class. One of the parameters is either char* or std::string. So I have to delete char*, and dont deletestd::string`. I have no idea what I should do.
template <typename T>
class Discipline
{
public:
unsigned int getLectureHours() const { return lecture_hours; }
unsigned int getTotalHours() const { return total_hours; }
unsigned int getPracticalHours() const { return practical_hours; }
unsigned int getSelfHours() const { return self_hours; }
T getName() const { return name; }
Date& getDate() const { return date; }
Discipline() : date(1,1,2000), name("Math"), total_hours(10), lecture_hours(4), practical_hours(4), self_hours(2) {}
Discipline(Date* tdate, T& tname, int& t1, int& t2, int& t3) : date(*tdate), name(tname), total_hours(t1), lecture_hours(t2), practical_hours(t3), self_hours(t1-t2-t3){}
Discipline(const Discipline<T>& other)
{
*this = other;
name = "def";
}
Discipline<char*>& operator=(const Discipline<char*>& param)
{
if (this != ¶m)
{
this->name = new char[strlen(param.name)+1];
strcpy(this->name, param.name);
this->date = param.date;
this->total_hours = param.total_hours;
this->lecture_hours = param.lecture_hours;
this->self_hours = param.self_hours;
this->practical_hours = param.practical_hours;
}
return *this;
}
Discipline<std::string>& operator=(const Discipline<std::string>& param)
{
if (this != ¶m)
{
// this->name = "";
// this->name += "def";
this->date = param.date;
this->total_hours = param.total_hours;
this->lecture_hours = param.lecture_hours;
this->self_hours = param.self_hours;
this->practical_hours = param.practical_hours;
}
return *this;
}
~Discipline<char*>() { delete[] name; }
private:
Date date;
T name;
unsigned int total_hours;
unsigned int lecture_hours;
unsigned int practical_hours;
unsigned int self_hours;
};
There is explicit specialization. In the implementation, you can go like
template<>
Discipline<string>::~Discipline(){}
template<>
Discipline<char*>::~Discipline(){
delete[] name;
}
This can be even done flexible:
template<class T>
Discipline<T>::~Discipline(){}
template<>
Discipline<char*>::~Discipline(){
delete[] name;
}
This variant would call delete on the class over char* and do nothing within the destructor in every other case, if you plan on adding more specializations in the future.
You might want to read http://en.cppreference.com/w/cpp/language/template_specialization
(Just to give an answer to the question as stated. Of course, the comment by antred is the actual solution.)
One variant is to add a simple static method , say DeleteTheString, with two argument overloads. Then you just call with your templated type value and let the compiler decide.
Another variant is to wrap the char* in a unique_ptr[], so it deletes itself. You could do that dynamically by having a little adaptor class SafeStore has member typedef std::string as, while SafeStore has the typedef std::unique_ptr<char[]> as.
I have this example :
class Entity
{
float mX;
float mY;
string mName;
// other attributes
public:
void setPosX(float x);
void setPosY(float y);
void setName(string name);
// other setters
template<typename T>
virtual void setAttributeValue(string param, T value)
{
if(param == "x")
setX(value);
else if(param == "y")
setY(value);
else if(param == "name")
setName(value);
// ...
}
};
class SpecialEntity : public Entity
{
int specialAttr;
// other special attributes
public:
void setSpecialAttr(int val);
// other setters
template<typename T>
virtual void setAttributeValue(string param, T value)
{
Entity::setAttributeValue(param, value);
if(param == "specialAttr")
setSpecialAttr(value);
// ...
}
};
This will not compile as templated virtual methods are not allowed.
I need this in my editor app, that has a Property Grid Control, depending on the name of the property in that control, I need to call a method from Entity class or an inherited class of Entity to set an attribute value.
What is the best way to acheive this.
When I've had to do this, one of the options I've used is to pass a boost::any rather than a T.
virtual void setAttributeValue(string param, boost::any value)
{
if(param == "x")
setX(boost::any_cast<int>(value));
else if(param == "y")
setY(boost::any_cast<int>(value));
else if(param == "name")
setName(boost::any_cast<std::string>(value));
// ...
}
do it the old fashioned way
setX(int)
setY(float)
setZ(string)
much safer (the compiler will spot errors) and faster
You can't make a template function like that, virtual or otherwise. All the function calls within each version of your template function have to compile even if you never intend to call them.
All that happens with template functions is a copy of the function is stamped out with T replaced with each type as needed. If you do it by hand it is easy to see that it will not compile. In this case there is no setX taking a string and no setName taking a float:
class Entity
{
float mX;
std::string mName;
public:
void setX(float x){ mX = x; }
void setName(std::string name){ mName = name; }
void setAttributeValue(std::string param, float value)
{
if(param == "x")
setX(value);
else if(param == "name")
setName(value); // Error!
}
void setAttributeValue(std::string param, std::string value)
{
if(param == "x")
setX(value); // Error!
else if(param == "name")
setName(value);
}
};
I suggest you make setAttributeValue virtual and non-templated and pass in a type that can be converted to any type. Perhaps a string, boost::any or boost::variant.
If you can't use boost::any or boost::variant you could create your own Value interface to pass in:
struct Value {
virtual float getFloat() const = 0;
virtual std::string getString() const = 0;
virtual int getInt() const = 0;
};
struct ValueBase : Value {
float getFloat() const override { throw std::runtime_error("Not float"); }
std::string getString() const override { throw std::runtime_error("Not string"); }
int getInt() const override { throw std::runtime_error("Not int"); }
};
struct FloatValue : ValueBase {
float value;
FloatValue(float value) : value(value){}
float getFloat() const override { return value; }
};
struct StringValue : ValueBase {
std::string value;
StringValue(std::string value) : value(value){}
std::string getString() const override { return value; }
};
struct IntValue : ValueBase {
int value;
IntValue(int value) : value(value){}
int getInt() const override { return value; }
};
class Entity {
float mX;
float mY;
std::string mName;
public:
void setX(float x);
void setY(float y);
void setName(std::string name);
virtual void setAttributeValue(std::string param, const Value& value) {
if(param == "x")
setX(value.getFloat());
else if(param == "y")
setY(value.getFloat());
else if(param == "name")
setName(value.getString());
}
};
class SpecialEntity : public Entity {
int specialAttr;
public:
void setSpecialAttr(int val);
void setAttributeValue(std::string param, const Value& value) override {
Entity::setAttributeValue(param, value);
if(param == "specialAttr")
setSpecialAttr(value.getInt());
}
};
Live demo.
You could use the curiously recurring template pattern, a polymorphic base class, some tagging and a polymorphic value class to achieve your desired functionality
First we need a polymorphic value type:
struct value{ virtual ~value(){} };
struct text_value: public value{ text_value(const std::string &str_): str(str_){} std::string str; };
struct int_value: public value{ int_value(int i_): i(i_){} int i; };
auto make_value(const std::string &str){ return text_value{str}; }
auto make_value(int i){ return int_value{i}; }
then our static (CRTP) and dynamic polymorphisms:
enum class entity_tag{
text
};
class entity{
public:
entity(entity_tag tag_): tag(tag_){}
virtual ~entity(){}
entity_tag tag;
};
template<typename Entity>
class entity_base: entity{
public:
entity_base(): entity(this->get_tag()){}
template<typename T>
void set_value(const std::string ¶m, T &&val){
reinterpret_cast<Entity*>(this)->set_value_impl(param, std::forward<T>(val));
}
static entity_tag get_tag(){
return Entity::get_tag_impl();
}
};
Then we can start defining some classes that implement our interface! (set_value_impl and get_tag_impl)
class text_entity: public entity_base<text_entity>{
protected:
std::string font = "times new roman";
int font_size = 10;
std::string text = "";
void set_text(value &&v){
auto tv = dynamic_cast<text_value&&>(v);
text = std::move(tv.str);
}
void set_font(value &&v){
auto tv = dynamic_cast<text_value&&>(v);
font = std::move(tv.str);
}
void set_font_size(value &&v){
auto iv = dynamic_cast<int_value&&>(v);
font_size = iv.i;
}
public:
static entity_tag get_tag_impl(){ return entity_tag::text; }
template<typename T>
void set_value_impl(const std::string &str, T &&val){
auto v = make_value(val);
if(str == "text")
set_text(std::move(v));
else if(str == "font")
set_font(std::move(v));
else if(str == "font_size")
set_font_size(std::move(v));
else
throw "invalid parameter";
}
};
It's as easy as 1, 2, 3!
You can use it the same way you wanted to in your question:
int main(){
text_entity text;
text.set_value("font", "comic sans");
text.set_value("font_size", 24);
text.set_value("text", "Comic sans sucks");
}
It also won't let you try to assign e.g. a float to "font" because there is not value type for float. If there were, it would throw an exception within set_font.
You could define the value subclasses within each entity subclass so that the compiler will always complain if a value is given that won't be possible for any given parameter.
Also you can store the entities inside of a container with other entity types:
int main(){
std::vector<entity*> entities;
text_entity text0;
text_entity text1;
pic_entity pic0;
slider_entity slider0;
entities.push_back(&text0);
entities.push_back(&text1);
entities.push_back(&pic0);
entities.push_back(&slider0);
}
But to access the set_value function of each entity you will need to have a look at the tag variable associated with each variable:
template<typename T>
void set_value_of_text(entity *e, const std::string ¶m, T &&t){
if(e->tag != entity_tag::text)
throw "entity is not a text entity";
dynamic_cast<text_entity*>(e)->set_value(param, std::forward<T>(t));
}
You could use something other than an enum class for the tag type to make it easier for future additions of entity types
I have many functions that do roughly the same apart from the what variable the modify
struct example
{
std::string name;
std::string category;
};
using ObjName = std::string;
using Value = std::string;
bool updateName(const ObjName &name, const Value& value) ...
bool updateCategory(const ObjName &name,const Value& value)
{
// boost optional pointing to struct reference
auto obj = findOjb(name);
if (obj)
{
obj.get().category = value; // variable name changes
return true;
}
return false;
}
What I am wondering is what I can do to combine the code ?
I suspect it will involve templates maybe traites/functors but I am unsure of how to approach it any ideas ?
Reworking Daerst's code to remove that awful offsetof in favor of pointers-to-members...
struct example
{
std::string name;
std::string category;
};
bool updateVariable(const ObjName &name, std::string example::*member, std::string const &value)
{
// your code ...
// Access
rule.get().*member = value
// rest of your code
}
bool updateName(const ObjName &oldname, const ObjName& newName)
{
return updateVariable(name, &example::name, newName));
}
bool updateCategory(const ObjName &name, Category &cat)
{
return updateVariable(name, &example::category, cat));
}
You could use lambdas:
template <typename Accessor>
bool updateVariable(const ObjName& name, const Value& value, Accessor access) {
auto obj = findObj(name);
if (obj)
{
access(obj.get()) = value;
return true;
}
return false;
}
bool updateCategory(const ObjName& name, const Value& value) {
return updateVariable(name, value,
[](Example& e) -> Value& { return e.category; });
}
This is a bit more flexible than the pointer-to-member solution. You can make it even more flexible by having the lambda do the setting instead of returning a reference.
You could use something traits like:
#include <string>
#include <assert.h>
struct example
{
std::string name;
int category;
};
struct nameDesc
{
typedef std::string valuetype;
static void set(example& obj, const valuetype& val)
{
obj.name = val;
}
};
struct categoryDesc
{
typedef int valuetype;
static void set(example& obj, const valuetype& val)
{
obj.category = val;
}
};
example test; // just for testing...
example& findObj(const std::string &name)
{
// just for testing...
return test;
}
template <typename V>
bool update(const std::string &objName, const typename V::valuetype& value)
{
example& obj = findObj(objName);
V::set(obj, value);
return true;
}
bool updateName(const std::string &objName, const std::string& value) { return update<nameDesc>(objName, value); }
bool updateCategory(const std::string &objName, int value) { return update<categoryDesc>(objName, value); }
int main()
{
update<nameDesc>("objname", "asdf");
update<categoryDesc>("objname", 1234);
assert(test.name == "asdf");
assert(test.category == 1234);
updateName("objname", "qwer");
updateCategory("objname", 7890);
assert(test.name == "qwer");
assert(test.category == 7890);
return 0;
}
And I'd encourage you having a look at boost::spirit / BOOST_FUSION_ADAPT_STRUCT if possible.
A bit hacky, but this could be a solution (untested code) using offsetof:
struct example
{
std::string name;
std::string category;
};
bool updateVariable(const size_t offset, std::string value)
{
// your code ...
// ASSIGNMENT: get address, apply offset and assign value
*(&rule.get() + offset) = cat;
// rest of your code
}
bool updateName(const ObjName &oldname, const ObjName& newName)
{
return updateVariable(offsetof(struct example, name), newName));
}
bool updateCategory(const ObjName &name, Category &cat)
{
return updateVariable(offsetof(struct example, category), cat));
}
I assume that ObjName and Category are typedefs of string or can be implicitly converted.
You still need to add a one-liner function for each member variable, which is pretty hard to elude in C++ if you want to stick to a hard-coded struct. You might want to consider converting the whole struct definition to data, e.g. loaded from a file, opening other possibilities.
I am wring a generic class to extract something of type SrcT by a string key, convert it to type TargetT and then return. Like:
class Foo
{
public:
bool get(const char* key, std::string& str)
{
if (std::string(key) == "found")
{
str = "stringA";
return true;
}
return false;
}
bool get(const char* key, int& a)
{
a = 100;
return true;
}
};
class Bar
{
public:
template <typename Converter>
typename Converter::result_type extract(const char* key, Converter converter)
{
typedef typename Converter::first_argument_type SrcT; // <- HERE IS THE ERROR
typedef typename Converter::result_type TargetT;
SrcT temp;
if (_foo.get(key, temp))
{
TargetT target = converter(temp);
return target;
}
else
{
throw std::runtime_exception("ah");
}
}
Foo _foo;
};
struct Converters
{
static int toInt(const std::string& str) { return str.size(); }
static float toFloat(int a) { return 100.0 + a; }
};
BOOST_AUTO_TEST_CASE(Nothing)
{
Bar bar;
const int saveHere = bar.extract("found", boost::bind(&Converters::toInt, _1));
BOOST_CHECK_EQUAL(saveHere, 7); // 7=sizeof("stringA")
}
TargetT was deduced from Converter type, but no clue about SrcT.
Any help is appreciated.
UPDATE
After checking boost/bind.hpp and boost/bind/bind_template.hpp, looks like no such thing was exposed.
Try with:
typedef typename boost::function_traits<Converter>::arg1_type SrcT;