What to use instead of templated virtual method in C++ - c++

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 &param, 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 &param, 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

Related

Namespace Implementation

I am trying to implement the functionality of namespaces. The main class Project contains a vector std::vector std::shared_ptr<Class>classes_{}; Each class, respectively, contains similar vectors with Variable and Function. Also, the Class class can have a base class - Class* parent_. The task is such, for example, when adding a new variable (in Class std::vector std::shared_ptr<Variable> vars_{};) or renaming an existing one, it was checked (taking into account visibility) that a variable with such a name does not exist in this class and all his ancestors. The names of the created functions and classes were also checked, etc.
Below is my implementation that I don't like.
Variable::parent_ can be both a class and a function. It is logical to assume that Class, if it does not have a parent class, parent_ was not nullptr, but Project*. In principle, this can be solved by having all classes store Nameable* parent_ rather than a concrete class. But then every time you access parent_ you would have to downcast the pointer.
Traversing all Nameable objects creates a set of temporary vectors (in Nameable::nameables() and in Nameable::get_nameables()).
Please advise how to solve these problems. Or perhaps where you can peep a more beautiful implementation
#include "string"
#include "string_view"
#include "vector"
#include <iostream>
#include <memory>
class Nameable
{
std::string name_{};
//VISIBILITY visibility_{ VISIBILITY::PUBLIC };
protected:
virtual Nameable* parent() const = 0;
virtual std::vector<std::shared_ptr<Nameable>> nameables() const = 0;
void get_nameables(std::vector<std::shared_ptr<Nameable>>& n)
{
std::vector<std::shared_ptr<Nameable>> names = nameables();
if (parent())
parent()->get_nameables(names);
n.insert(n.end(), names.begin(), names.end());
}
public:
Nameable(std::string_view name) : name_(name)
{
//if (!check_name(name)) throw InvalidNameException(name);
}
virtual ~Nameable() {};
std::shared_ptr<Nameable> check_name(std::string_view name) const
{
return std::shared_ptr<Nameable>();
}
const std::string& get_name() const& { return name_; }
void set_name(std::string_view name) { name_ = name; }
};
class Variable;
class Function;
class Class final : public Nameable
{
std::vector<std::shared_ptr<Variable>> vars_{};
std::vector<std::shared_ptr<Function>> funs_{};
Class* parent_{};
Nameable* parent() const override
{
return parent_;
}
std::vector<std::shared_ptr<Nameable>> nameables() const override
{
std::vector<std::shared_ptr<Nameable>> n{};
n.insert(n.end(), vars_.begin(), vars_.end());
n.insert(n.end(), funs_.begin(), funs_.end());
return n;
}
public:
Class(std::string_view name, Class* parent = nullptr)
: Nameable(name), parent_(parent)
{}
std::shared_ptr<Variable> create_var(std::string_view name)
{
vars_.push_back(std::make_shared<Variable>(name, this));
return vars_.back();
}
std::shared_ptr<Function> create_fun(std::string_view name)
{
funs_.push_back(std::make_shared<Function>(name, this));
return funs_.back();
}
};
class Variable final : public Nameable
{
Class* parent_{};
Nameable* parent() const override
{
return parent_;
}
std::vector<std::shared_ptr<Nameable>> nameables() const override
{
std::vector<std::shared_ptr<Nameable>> n{};
return n; // return empty vector
}
public:
Variable(std::string_view name, Class* parent)
: Nameable(name), parent_(parent) {}
};
class Function final : public Nameable
{
Class* parent_{};
std::vector<std::shared_ptr<Variable>> local_vars_{};
Nameable* parent() const override
{
return parent_;
}
std::vector<std::shared_ptr<Nameable>> nameables() const override
{
std::vector<std::shared_ptr<Nameable>> n{};
n.insert(n.end(), local_vars_.begin(), local_vars_.end());
return n;
}
public:
Function(std::string_view name, Class* parent)
: Nameable(name), parent_(parent) {}
};
class Project final : public Nameable
{
std::vector<std::shared_ptr<Class>> classes_{};
Nameable* parent() const override { return nullptr; }
std::vector<std::shared_ptr<Nameable>> nameables() const override
{
std::vector<std::shared_ptr<Nameable>> n{};
n.insert(n.end(), classes_.begin(), classes_.end());
return n;
}
public:
Project(std::string_view name) : Nameable(name){}
};
I'm not sure if I understand your intent clearly, yet maybe use composition instead? It's just easier to maintain. It'd be easier for me if you have provided any use case, but nonetheless I have something like below for you.
// You may want use it in a bitfield mask manner.
enum class NameType : int8_t {
Function = 1,
Variable = 2,
Object = 4,
None = 8
};
// Just simple storage, defaulted if constructed with a default constructor.
struct Nameable {
// union := variable | static_function
using Parent = std::variant<std::optional<std::any>, std::function<std::any(std::any)>>;
explicit Nameable(std::string_view sv, NameType t, const Parent& p = std::nullopt): name(sv), type(t), parent(p)
{
}
std::string name{};
NameType type = NameType::None;
Parent parent = std::nullopt;
};
// Flat aggregator of names (no hierarchy mapping), one-for-all (e.g. per class, per namespace).
struct Collector {
using Nameables = std::vector<std::optional<Nameable>>;
explicit Collector(std::string_view sv, NameType t, const std::optional<std::any>& p = std::nullopt)
{
attach(sv, t, p);
}
void attach(std::string_view sv, NameType t, const std::optional<std::any>& p = std::nullopt)
{
auto n = Nameable(sv, t, p);
names.emplace_back(std::move(n));
}
// combine collectors, such that (f+g)(n) := f(n) + g(n), e.g. for namespace, module, translation unit.
[[maybe_unused]] void attach(const Collector& c)
{
for (const auto& name: c.names) {
attach(name->name, name->type, name->parent);
}
}
bool isUniqueName(std::string_view key) const
{
return std::ranges::all_of(names, [key](const auto& name) {
if (name.has_value() && name.value().name == key) return false;
return true;
});
}
bool isUniqueObjectName(std::string_view key) const
{
return std::ranges::all_of(names, [key](const auto& name) {
if (name.has_value() && name.value().type == NameType::Object && name.value().name == key) return false;
return true;
});
}
std::size_t index(std::string_view key) const
{
for (std::size_t i = 0; i != names.size(); ++i) {
const auto& name = names[i];
if (name.has_value() && name.value().name == key) return i;
}
return std::string::npos;
}
void rename(std::string_view from, std::string_view to)
{
names.at(index(from)).value().name = std::string(to);
}
const Nameable& get(std::string_view key) const
{
if (!names.at(index(key)).has_value()) throw std::bad_optional_access();
return names.at(index(key)).value();
}
std::any variable(std::string_view name) const
{
const auto& nameable = get(name);
if (const auto& var = nameable.parent; std::holds_alternative<std::optional<std::any>>(var)) {
if (const auto& opt = std::get<0>(var); opt.has_value()) return opt.value();
throw std::bad_optional_access();
}
throw std::bad_variant_access();
}
Nameables names{};
};
The idea is to collect names hierarchically but through "formation". Alternatively, just use trees, or dictionaries (as it has been already stated).
namespace outer {
Collector cOuter;
constexpr int GLOBAL = 1;
namespace inner {
Collector cInner;
void f() {}
namespace internal {
Collector cInternal;
Data d{};
}
}
}
I'm not certain if you're really interested in storing objects, though.
With std::any it may require some gymnastic, but you can handle it.
I would suggest std::optional, but this need trivially copyable structure, so be careful with adding new fields. Collecting member functions require some additional std::function signature. If you need more information, e.g. about types, then store them as type traits, or concepts, or anyhow. Eventually, introduce std::variant<Variable, Function, Class/Object>. Let give them do their job (Variable/+types, Function/+params, Class/+hierarchy) and compose them at the end.
struct SomeClass {
static auto Something(int y = 3) noexcept
{
return 1 + y;
}
int var1 = 0;
float var2 = 5.5;
};
SomeClass s{};
auto c = Collector("SomeClass", NameType::Object, s);
c.attach("var1", NameType::Variable, s.var1);
c.attach("var2", NameType::Variable, s.var2);
c.attach("Something", NameType::Function, SomeClass::Something);
std::cout << c.names[0].value().name << '\n'; // "SomeClass", etc.
std::cout << std::boolalpha << c.isUniqueName("var1") << '\n'; // false
c.rename("var1", "var_1");
std::cout << std::boolalpha << c.isUniqueName("var1") << '\n'; // true
std::cout << std::boolalpha << c.isUniqueObjectName("SomeClass") << '\n'; // false
auto var2 = c.variable("var2");
auto vf = std::any_cast<float>(var2);
std::cout << std::to_string(vf) << '\n'; // 5.500000

Make a Class variable const after declaration (run time) (with some class method)

I have a template:
template<typename T>
struct Parameter {
T value;
std::string name;
Parameter(std::string name, T value) : name(name), value(value){}
void fix() {
// Fix this->value (make this->value const)
}
void print() { std::cout << value << std::endl; }
};
and I would like at some point after initialization to 'const-ify' the value variable
std::string name = "variance";
double var = 1.0;
Parameter<double> variance(name, var);
variance.print();
variance.fix();
variance.value = 2.3; // Not Allowed, throws error
Is it possible to do so and how?
If you want to maintain the same interface, and marshalling access to value through accessors is something you want to avoid, then you could isolate the "fixable" feature in its own dedicated type that implicitly converts to/from T:
template<typename T>
class fixable {
bool fixed_ = false;
T val_;
public:
fixable() = default;
fixable(T v) : val_(v) {}
fixable(const fixable&) = default;
fixable(fixable&&) = default;
operator const T&() const {
return val_;
}
fixable& operator=(const T& v) {
if(fixed_ ) {
throw std::runtime_error("Fixable has been fixed");
}
val_ = v;
return *this;
}
void fix() {
fixed_ = true;
}
};
You would then replace the T member with a fixable<T> within Parameter:
template<typename T>
struct Parameter {
fixable<T> value;
std::string name;
Parameter(std::string name, T value) : name(name), value(value){}
void fix() {
value.fix();
}
void print() { std::cout << value << std::endl; }
};
The main function from your question can remain exactly as-is.
You can use something like this:
Similar to abowe answer but with boolean inside the Parameter struct
template<typename T>
struct Parameter {
Parameter(std::string name, T value) : name(name), value(value), bFixed(false) {}
void fix() {
bFixed = true;
}
void print() { std::cout << value << std::endl; }
Parameter& operator=(const T& oValue)
{
if (bFixed)
{
throw std::runtime_error("Error fixed value..");
}
value = oValue;
return *this;
}
std::string name;
private:
bool bFixed;
T value;
};
int main()
{
std::string name = "variance";
double var = 1.0;
Parameter<double> variance(name, var);
variance.print();
variance.fix();
variance = 2.3; // Not Allowed, throws error
}
You cannot change a member variable from const to non-const. However, you can create a new object in which it is const. For example:
template<typename T>
struct example {
T value;
example<T const> fix() && {
return {value};
}
};
int main(){
auto x = example<int>{1};
x.value = 4; // OK
auto y = std::move(x).fix();
y.value = 7; // error: assignment of read-only member
}
The presence of && forces the use of std::move which makes it obvious that x should no longer be used.

dynamically call same named function with different return type

I have a situation here...
I want to design a Factory where I can call a function with same name and no parameters but return different data Types. Based on the SubClassName I need to instantiate the Object.
Need help or lead on any design pattern to follow?
EDIT:
An abstract pseudo code...
class parent{
public:
virtual string getName() = 0;
//some virtual function.. not sure how to design. As the return type is dynamic.
*** getValue(){}
};
class A : public parent{
int x;
public:
virtual string getName(){ return "A";}
virtual int getValue(){retun x;}
};
class B : public parent{
string s;
public:
virtual string getName(){ return "B";}
virtual string getValue(){ return s;}
};
void main(){
string callingClass = "B";
parent * arrayPtrs[2];
arrayPtrs[0] = new A;
arrayPtrs[1] = new B;
for (loop through array, through iterator i){
if(arrayPtrs[i]->getName == callingClass ){
cout<<arrayPtrs[i]->getValue;
}
}
}
In C++ a function can only have one return type at a time, and you cannot change that dynamically.
However - as suggested by #mch - you can use template specializations. Keep in mind though, that this method is not dynamic. Your functions will be generated at compile time.
If I understood your question correctly, maybe this can be of help.
class MyObject1
{
//...
};
class MyObject2
{
//...
};
template<typename T>
struct Factory
{
constexpr static T gen();
};
template<>
struct Factory<MyObject1>
{
constexpr static MyObject1 gen()
{
return MyObject1(/*... whatever parameters you see fit ...*/);
}
};
template<>
struct Factory<MyObject2>
{
constexpr static MyObject2 gen()
{
return MyObject2(/*... whatever parameters you see fit ...*/);
}
};
int main()
{
auto myObj = Factory<MyObject1>::gen();
return 0;
}
Although this method seems fairly useless to me. You could simply call the desired constructor instead of this.
But then again, I'm not sure if this is what you thought of. If I made any mistakes please feel free, to correct me. I'll try to edit my answer best as I can.
EDIT:
To keep the virtual functionality too, the only way I can think of is type erasure: see https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Type_Erasure
The closest I could get to what you've asked for is this:
#include <iostream>
#include <string>
#include <any>
class parent {
public:
// you can use this too but I think type checking is more handy
// see in main function
/* virtual std::string getName() const = 0; */
virtual std::any getValue() const = 0;
};
class A : public parent {
public:
typedef int value_type;
private:
value_type x;
public:
A(value_type x) :
x(x)
{}
/* virtual std::string getName() const override { return "A"; } */
virtual std::any getValue() const override
{ return this->x; }
};
class B : public parent {
public:
typedef std::string value_type;
private:
value_type s;
public:
B(const value_type& s) :
s(s)
{}
/* virtual std::string getName() const override { return "B"; } */
virtual std::any getValue() const override
{ return this->s; }
};
int main(){
using callingClass = A;
parent* arrayPtrs[2];
arrayPtrs[0] = new A(42);
arrayPtrs[1] = new B("my string");
for (unsigned i = 0; i < sizeof(arrayPtrs) / sizeof(parent*); ++i)
{
// Note:
// dynamic cast will return nullptr if $callingClass
// is not a derived class
if (dynamic_cast<callingClass*>(arrayPtrs[i]))
std::cout << std::any_cast<callingClass::value_type>(arrayPtrs[i]->getValue()) << std::endl;
}
return 0;
}
I hope this one helps.
Note, that I used dynamic_cast to check the correct type. If you know a better solution, you can use that, too. But under these circumstances I couldn't think of any better.
EDIT2:
#include <iostream>
#include <string>
#include <tuple>
class some
{
using id = size_t;
template<typename T>
struct type { static void id() { } };
template<typename T>
static id type_id() { return reinterpret_cast<id>(&type<T>::id); }
template<typename T>
using decay = typename std::decay<T>::type;
template<typename T>
using none = typename std::enable_if<!std::is_same<some, T>::value>::type;
struct base
{
virtual ~base() { }
virtual bool is(id) const = 0;
virtual base *copy() const = 0;
} *p = nullptr;
template<typename T>
struct data : base, std::tuple<T>
{
using std::tuple<T>::tuple;
T &get() & { return std::get<0>(*this); }
T const &get() const& { return std::get<0>(*this); }
bool is(id i) const override { return i == type_id<T>(); }
base *copy() const override { return new data{get()}; }
};
template<typename T>
T &stat() { return static_cast<data<T>&>(*p).get(); }
template<typename T>
T const &stat() const { return static_cast<data<T> const&>(*p).get(); }
template<typename T>
T &dyn() { return dynamic_cast<data<T>&>(*p).get(); }
template<typename T>
T const &dyn() const { return dynamic_cast<data<T> const&>(*p).get(); }
public:
some() { }
~some() { delete p; }
some(some &&s) : p{s.p} { s.p = nullptr; }
some(some const &s) : p{s.p->copy()} { }
template<typename T, typename U = decay<T>, typename = none<U>>
some(T &&x) : p{new data<U>{std::forward<T>(x)}} { }
some &operator=(some s) { swap(*this, s); return *this; }
friend void swap(some &s, some &r) { std::swap(s.p, r.p); }
void clear() { delete p; p = nullptr; }
bool empty() const { return p; }
template<typename T>
bool is() const { return p ? p->is(type_id<T>()) : false; }
template<typename T> T &&_() && { return std::move(stat<T>()); }
template<typename T> T &_() & { return stat<T>(); }
template<typename T> T const &_() const& { return stat<T>(); }
template<typename T> T &&cast() && { return std::move(dyn<T>()); }
template<typename T> T &cast() & { return dyn<T>(); }
template<typename T> T const &cast() const& { return dyn<T>(); }
template<typename T> operator T &&() && { return std::move(_<T>()); }
template<typename T> operator T &() & { return _<T>(); }
template<typename T> operator T const&() const& { return _<T>(); }
};
using any = some;
class parent {
public:
// you can use this too but I think type checking is more handy
/* virtual std::string getName() const = 0; */
virtual any getValue() const = 0;
};
class A : public parent {
public:
typedef int value_type;
private:
value_type x;
public:
A(value_type x) :
x(x)
{}
/* virtual std::string getName() const override { return "A"; } */
virtual any getValue() const override
{ return this->x; }
};
class B : public parent {
public:
typedef std::string value_type;
private:
value_type s;
public:
B(const value_type& s) :
s(s)
{}
/* virtual std::string getName() const override { return "B"; } */
virtual any getValue() const override
{ return this->s; }
};
int main(){
using callingClass = A;
parent* arrayPtrs[2];
arrayPtrs[0] = new A(42);
arrayPtrs[1] = new B("my string");
for (unsigned i = 0; i < sizeof(arrayPtrs) / sizeof(parent*); ++i)
{
// Note:
// dynamic cast will return nullptr if $callingClass
// is not a derived class
if (dynamic_cast<callingClass*>(arrayPtrs[i]))
std::cout << arrayPtrs[i]->getValue()._<callingClass::value_type>() << std::endl;
}
return 0;
}
This snipped is in case you cannot use C++17 features, and is based on:
any class

Determine Class Implementation Dynamically via Constructor

I want to create a class which behaves a certain way - e.g. spits out certain values from a function double getValue(const int& x) const - based on a "type" that was passed into its constructor. Right now I have two methods:
Store the passed-in "type" and then evaluate a switch statement in getValue each time it is called in order to decide which implementation to use.
Use a switch statement on the passed-in "type" (in the constructor) to create an internal object that represents the desired implementation. So no switch required anymore in getValue itself.
Method 1 "appears" inefficient as switch is called every time I call getValue. Method 2 seems somewhat clunky as I need to utilise <memory> and it also makes copying/assigning my class non-trivial.
Are there any other cleaner methods to tackle a problem like this?
Code Example:
#include <memory>
enum class ImplType { Simple1, Simple2 /* more cases */ };
class MyClass1
{
private:
const ImplType implType;
public:
MyClass1(const ImplType& implType) : implType(implType) { }
double getValue(const int& x) const
{
switch (implType)
{
case ImplType::Simple1: return 1; /* some implemention */
case ImplType::Simple2: return 2; /* some implemention */
}
}
};
class MyClass2
{
private:
struct Impl { virtual double getValue(const int& x) const = 0; };
struct ImplSimple1 : Impl { double getValue(const int& x) const override { return 1; /* some implemention */ } };
struct ImplSimple2 : Impl { double getValue(const int& x) const override { return 2; /* some implemention */ } };
const std::unique_ptr<Impl> impl;
public:
MyClass2(const ImplType& implType) : impl(std::move(createImplPtr(implType))) { }
static std::unique_ptr<Impl> createImplPtr(const ImplType& implType)
{
switch (implType)
{
case ImplType::Simple1: return std::make_unique<ImplSimple1>();
case ImplType::Simple2: return std::make_unique<ImplSimple2>();
}
}
double getValue(const int& x) const { return impl->getValue(x); }
};
int main()
{
MyClass1 my1(ImplType::Simple1);
MyClass2 my2(ImplType::Simple1);
return 0;
}
Your code is basically mimicing a virtual method (sloppy speaking: same interface but implementation is chosen at runtime), hence your code can be much cleaner if you actually do use a virtual method:
#include <memory>
struct base {
virtual double getValue(const int& x) const = 0;
};
struct impl1 : base {
double getValue(const int& x) { return 1.0; }
};
struct impl2 : base {
double getValue(const int& x) { return 2.0; }
};
// ... maybe more...
enum select { impl1s, impl2s };
base* make_impl( select s) {
if (s == impl1s) return new impl1();
if (s == impl2s) return new impl2();
}
int main() {
std::shared_ptr<base> x{ make_impl(impl1) };
}
Not sure if this is what you are looking for. By the way, using <memory> should not make you feel "clunky", but instead you should feel proud that we have such awesome tools in c++ ;).
EDIT: If you dont want the user to work with (smart-)pointers then wrap the above in just another class:
struct foo {
shared_ptr<base> impl;
foo( select s) : impl( make_impl(s) ) {}
double getValue(const int& x) { return impl.getValue(x); }
};
now a user can do
int main() {
auto f1 { impl1s };
auto f2 { impl2s };
f1.getValue(1);
f2.getValue(2);
}
If you have a closed set of types you can choose from, you want std::variant:
using MyClass = std::variant<MyClass1, MyClass2, MyClass3, /* ... */>;
It doesn't use dynamic allocation - it's basically a type-safe modern alternative to union.
More object-oriented approach:
class Interface
{
public:
virtual int getValue() = 0;
};
class GetValueImplementation1 : public Interface
{
public:
int getValue() {return 1;}
};
class GetValueImplementation2 : public Interface
{
public:
int getValue() {return 2;}
};
class GeneralClass
{
public:
GeneralClass(Interface *interface) : interface(interface) {}
~GeneralClass()
{
if (interface)
delete interface;
}
int getValue() { return interface->getValue(); }
private:
Interface *interface;
};
So, in this case you can use it without any pointers:
int main()
{
GeneralClass obj1(new GetValueImplementation1());
GeneralClass obj2(new GetValueImplementation2());
cout << obj1.getValue() << " " << obj2.getValue();
return 0;
}
The output will be:
1 2
But in the case you should be careful with null pointers or use smart ones inside GeneralClass.

Partial specialization of member functions

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 &section, 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