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.
Related
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 am in need to (or better, I have the chance to) refactor some code in order to make it cleaner.
I would like to use some template, as I think this is a good candidate, in order to reduce the code duplication.
Here is my hpp
class Monetary
{
public:
Monetary();
Monetary(const rapidjson::Value& iMonetary);
virtual ~Monetary();
[...cut...]
private:
static void initMember(const rapidjson::Value& iMonetary, const char* iName, int& oMember);
static void initMember(const rapidjson::Value& iMonetary, const char* iName, std::string& oMember);
private:
int _amount;
int _decimal_place;
std::string _currency;
std::string _type;
};
And here is the implementation for the the initMember methods:
static void Monetary::initMember(const rapidjson::Value& iMonetary, const char* iName, int& oMember)
{
rapidjson::Value::ConstMemberIterator aIterator;
aIterator = iMonetary.FindMember(iName);
if ( aIterator != iMonetary.MemberEnd() &&
aIterator->value.IsNumber() )
{
oMember = iMonetary[iName].GetInt();
}
}
static void Monetary::initMember(const rapidjson::Value& iMonetary, const char* iName, std::string& oMember)
{
rapidjson::Value::ConstMemberIterator aIterator;
aIterator = iMonetary.FindMember(iName);
if ( aIterator != iMonetary.MemberEnd() &&
aIterator->value.IsNumber() )
{
oMember = iMonetary[iName].GetString();
}
}
I was thinking about writing something like
template<typename T>
void Monetary::initMember(const rapidjson::Value& iMonetary, const char* iName, T& oMember)
{
rapidjson::Value::ConstMemberIterator aIterator;
aIterator = iMonetary.FindMember(iName);
if (aIterator == iMonetary.MemberEnd())
{
return;
//throw monetaryException
}
assignFromValue(iMonetary[iName], oMember);
}
template<>
void Monetary::assignFromValue<int>(const rapidjson::Value& iValue, int& oMember)
{
if (!iValue.IsNumber())
{
return;
//throw monetaryException
}
oMember = iValue.GetInt();
}
template<>
void Monetary::assignFromValue<std::string>(const rapidjson::Value& iValue, std::string& oMember)
{
if (!iValue.IsString())
{
return;
//throw monetaryException
}
oMember = iValue.GetString();
}
Is there any clewer way to do that ?
My suggestions:
You don't need to make assignFromValue member functions. If you can implement functionality using non-member functions, you should prefer non-member functions. See How Non-Member Functions Improve Encapsulation and How Non-Member Functions Improve Encapsulation.
You don't need to make assignFromValue function templates. They can be simple overloads.
void assignFromValue(const rapidjson::Value& iValue,
int& oMember)
{
if (!iValue.IsNumber())
{
return;
//throw monetaryException
}
oMember = iValue.GetInt();
}
void assignFromValue(const rapidjson::Value& iValue,
std::string& oMember)
{
if (!iValue.IsString())
{
return;
//throw monetaryException
}
oMember = iValue.GetString();
}
I think I'd do it with a tag-dispatched converter object:
#include <string>
#include <type_traits>
#include <stdexcept>
// simulate your value class
struct Value
{
bool IsNumber() const;
bool IsString() const;
std::string getString() const;
int getInt() const;
};
// a tag type for easy tag dispatching
template<class Type> struct tag {};
// a converter object contains all rules and conversion sequences
struct ValueConverter
{
std::string operator()(tag<std::string>, const Value& v) const
{
if (not v.IsString()) throw std::invalid_argument("not a string");
return v.getString();
}
int operator()(tag<int>, const Value& v) const
{
if (not v.IsNumber()) throw std::invalid_argument("not a number");
return v.getInt();
}
};
// write the member once
template<class Target>
void initMember(const Value& iMonetary, const char* iName, Target& oMember)
{
using target_type = std::decay_t<Target>;
auto converter = ValueConverter();
oMember = converter(tag<target_type>(), iMonetary);
}
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;
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
Consider the following example:
struct MyStruct {
int a;
int b;
};
I can use macros to set a member from an instance of the struct by doing this:
#define setVar(x,y) instance.x = y
then in any function I can say:
setVar(a, 4)
How can I send in a as a string to the macro? Is that also possible?
setVar("a", 4)
EDIT: There are a bunch of predefined structs with members that are all of type double. I only know what struct I am using by an XML config file that is passed in. After parsing, I have a bunch of strings that are a list of all the data members and values that need to be set. I need to use this list to set values for each of the members in the struct.
It is only possible if you define the struct itself using some macro, for example:
#define MY_STRUCT_STRUCTURE FIELD(a) FIELD(b) FIELD(d) FIELD(e) FIELD(f)
struct MyStruct {
# define FIELD(name) int name;
MY_STRUCT_STRUCTURE
# undef FIELD
bool setVar(char* fieldname, int val)
{
# define FIELD(name) if(strcmp(#name,fieldname)==0){name=val; return true;};
MY_STRUCT_STRUCTURE
# undef FIELD
return false; // name not found
}
};
int main()
{
MyStruct s;
s.setVar("a",1);
s.setVar("b",2);
s.setVar("f",100);
}
I have coded some quick and dirty code, but could give you some ideas, hope that helps. The main trick here is too use unions.
struct MyStruct
{
int a;
double b;
MyStruct()
: a(0), b(0) {}
};
MyStruct instance;
union value
{
long value_a;
double value_d;
} myvalue;
void blah_a(value v)
{
instance.a = v.value_a;
}
void blah_b(value v)
{
instance.b = v.value_d;
}
struct
{
(void)(*fn)(value);
const char* key;
}
lookup_table[] =
{
{ &blah_a, "a" },
{ &blah_b, "b" }
};
void setVar(const char* c, value v)
{
for (int i = 0; lookup_table[i].fn; i++)
if (c == lookup_table[i].key)
(*(lookup_table[i].fn))(v);
}
int main(int argc, char* argv[])
{
value v;
v.value_a = 6;
setVar("a", v);
return 0;
}
Might not be what you are looking for but an alternative solution to macros etc.. would just be some encapsulation and OO design. You can change the Field class to a template later and you will be able to represent anything basically.
You can create a class
class Field
{
public:
Field(const std::string& name, const std::string& type);
virtual ~Field(void);
std::string toString() const;
std::string getName() const;
int getValue() const { return value };
private:
std::string name;
std::string type;
int value;
};
And then a structure class
#pragma once
#include <boost/ptr_container/ptr_deque.hpp>
#include <string>
class Field;
class MyStructure
{
public:
typedef boost::ptr_deque<Field> FieldList;
typedef FieldList::iterator FieldListIter;
typedef FieldList::auto_type AutoField;
MyStructure(void);
MyStructure(const std::string& name);
virtual ~MyStructure(void);
void setName(const std::string& name);
std::string getName() const;
void addField( std::auto_ptr<Field> field );
std::string getFieldValue( const std::string& name ) const;
MyStructure::AutoField removeField( const std::string& name );
std::string toString(void) const;
private:
std::string struct_name;
FieldList fields;
};
And then to use it:
auto_ptr<MySructure> struct_a(new MySructure("StructName1",0) );
struct_a->addField( auto_ptr<Field> ( new Field( "Field1", 1 ) ) );
struct_a->addField( auto_ptr<Field> ( new Field( var_str1, 2) ) );
struct_a->addField( auto_ptr<Field> ( new Field( getName(), getVal() ) ) );