I'm sure this question comes up a lot, but my question has a slight difference which I haven't seen addressed (perhaps this is due to poor design on my part: I'm pretty new to C++).
For example, let's say I have a base class as such:
class Var {
public:
Var(std::string id, std::string description) : _id(id), _description(description) {}
private:
std::string _id, _description;
public:
std::string id() {
return this->_id;
}
std::string description() {
this->_description;
}
};
And some implementations like this:
template<
typename N,
typename = typename std::enable_if<std::is_arithmetic<N>::value, N>::type
>
class VarNumber : public Var {
public:
VarNumber(std::string id, std::string desc, N value, N min, N max) : Var(id, desc), _value(value), _min(min), _max(max) {}
VarNumber(std::string id, N value, N min, N max) : VarNumber(id, "", value, min, max) {}
private:
N _value;
N _min, _max;
public:
N value() {
return this->_value;
}
void set(N value) {
this->_value = std::clamp(value, this->_min, this->_max);
}
N min_value() {
return this->_min;
}
N max_value() {
return this->_max;
}
};
class VarBool : public Var {
public:
VarBool(std::string id, std::string desc, bool value) : Var(id, desc), _value(value) {}
VarBool(std::string id, bool value) : VarBool(id, "", value) {}
private:
bool _value;
public:
bool value() {
return this->_value;
}
bool set_value(bool value) {
this->_value = value;
}
bool toggle() {
this->set_value(!this->_value);
}
};
Say I wanted a container to hold all implementations of Var in one place. Now, I've been told that abusing inheritance to do this is not the way to go, i.e.:
std::vector<Var*> vars;
vars.push_back(new VarBool(...));
vars.push_back(new VarNumber<float>(...));
Especially since the above would require a cast to access methods specific to each Var implementation:
(static_cast<VarBool*>(vars.at(0)))->toggle();
float max = (static_cast<VarNumber<float>*>(vars.at(1)))->max_value());
I've also tried defining the container like:
std::vector<std::variant<VarBool, VarNumber<int>, VarNumber<float>, ... >>
This would work if there was a finite number of types to be templated for each Var implementation, but that doesn't work for my purposes. I have other Var types that are templated for enums, that cannot be defined at compile time:
template<
typename E,
typename = typename std::enable_if<std::is_enum<E>::value>::type
>
class VarMOption : public Var {
...
};
What's the best way to go about this? I've tried implementing a type erasure pattern but that only works if each "child" has the same methods.
I'd like to design this in a way that while storing all types in a single container, there are no pointless states represented, i.e. a max_value() method on a VarBool, or a toggle() method on a VarNumber<float>.
Is this a result of poor design? What's the best approach for this?
Thanks.
EDIT: Example regarding #ALX23z's comment
enum Target {
FOO,
BAR,
QAZ
}
std::unique_ptr<Var> enum_var = std::make_unique(Var("test enum", "description"));
enum_var->variant = EnumVar<Target>(Target::FOO); //EnumVar doesn't exist, since we would have to somehow define it in the variant
// But this would work:
std::unique_ptr<Var> num_var = std::make_unique(Var("test num", "desc"));
num_var->variant = NumberVar<float>(3.f, 0.f, 10.f);
// In Var class:
class Var {
// constructor here... (2 strs)
std::variant<NumberVar<float>, NumberVar<int> .. etc> variant;
}
EDIT 2
Here's what I came up with. It's very ugly.
class VarBool {
public:
VarBool(bool value_) : value_(value_) {}
private:
bool value_;
public:
bool& value() {
return this->value_;
}
void set(const bool& other) {
this->value_ = other;
}
};
template<
typename N,
typename = typename std::enable_if<std::is_arithmetic<N>::value, N>::type
>
class VarNumber {
public:
VarNumber(N value_, N min_, N max_) : value_(value_), min_(min_), max_(max_) {}
private:
N value_, min_, max_;
public:
N& value() {
return this->value_;
}
void set(const N& other) {
this->value_ = std::clamp(other, this->min_, this->max_);
}
N minimum() {
return this->min_;
}
N maximum() {
return this->max_;
}
};
class VarWrapper {
using V = std::variant<VarBool, VarNumber<int>, VarNumber<float>>;
public:
VarWrapper(std::string id_, std::string desc_, V inner_) : id_(id_), desc_(desc_), inner_(inner_) {}
VarWrapper(std::string id_, V inner_) : VarWrapper(id_, "", inner_) {}
private:
std::string id_, desc_;
V inner_;
public:
std::string id() {
return this->id_;
}
std::string desc() {
return this->desc_;
}
template<typename T>
T& unwrap() {
if (std::holds_alternative<T>(this->inner_))
return std::get<T>(this->inner_);
}
};
Usage:
std::shared_ptr<VarWrapper> w_var = std::make_shared<VarWrapper>(VarWrapper("name", "description", VarNumber<float>(1.5f, 0.f, 10.f)));
float& unwrapped_var_val = w_var->unwrap<VarNumber<float>>().value();
std::cout << unwrapped_var_val << "\n"; //prints 1.5f
w_var->unwrap<VarNumber<float>>().set(200.f);
std::cout << unwrapped_var_val << "\n"; //prints 10.f (clamped)
Related
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
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.
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
I'm trying to implement a simple abstract syntax tree (AST) in C++ using the visitor pattern. Usually a visitor pattern does not handle return value. But in my AST there are expressions nodes which care about the return type and value of its children node. For example, I have a Node structure like this:
class AstNode
{
public:
virtual void accept(AstNodeVisitor&) = 0;
void addChild(AstNode* child);
AstNode* left() { return m_left; }
AstNode* right() { return m_right; }
...
private:
AstNode* m_left;
AstNode* m_right;
};
class CompareNode : public AstNode
{
public:
virtual void accept(AstNodeVisitor& v)
{
v->visitCompareNode(this);
}
bool eval(bool lhs, bool rhs) const
{
return lhs && rhs;
}
};
class SumNode : public AstNode
{
public:
virtual void accept(AstNodeVisitor& v)
{
v->visitSumNode(this);
}
int eval(int lhs, int rhs) const
{
return lhs + rhs;
}
};
class AstNodeVisitor
{
public:
...
bool visitCompareNode(CompareNode& node)
{
// won't work, because accept return void!
bool lhs = node.left()->accept(*this);
bool rhs = node.right()->accept(*this);
return node.eval(lhs, rhs);
}
int visitSumNode(Node& node)
{
// won't work, because accept return void!
int lhs = node.left()->accept(*this);
int rhs = node.right()->accept(*this);
return node.eval(lhs, rhs);
}
};
In this case both CompareNode and SumNode are binary operators but they rely on the return type of their children's visit.
As far as I can see to make it work, there are only 2 options:
accept can still return void, save the return value in a context object which is passed to each accept and visit function, and use them in the visit function, where I know what type to use. This should work but feels like a hack.
make AstNode a template, and accept function a none virtual, but return type depends on template parameter T.But if I do this, I no longer have a common AstNode* class and can't save any AstNode* in the children list.
for example:
template <typename T`>
class AstNode
{
public:
T accept(AstNodeVisitor&);
...
};
So is there a more elegant way to do this? This should be a fairly common problem for people implementing AST walking so I'd like to know what's the best practice.
Thanks.
The Visitor can have member that it can use to store result, something like:
class AstNodeVisitor
{
public:
void visitCompareNode(CompareNode& node)
{
node.left()->accept(*this); // modify b
bool lhs = b;
node.right()->accept(*this); // modify b
bool rhs = b;
b = node.eval(lhs, rhs);
}
void visitSumNode(Node& node)
{
node.left()->accept(*this); // modify n
int lhs = n;
node.right()->accept(*this); // modify n
int rhs = n;
n = node.eval(lhs, rhs);
}
private:
bool b;
int n;
};
You may also want to save the type of last result or use something like boost::variant.
template<class T> struct tag { using type=T; };
template<class...Ts> struct types { using type=types; }
template<class T>
struct AstVisitable {
virtual boost::optional<T> accept( tag<T>, AstNodeVisitor&v ) = 0;
virtual ~AstVisitable() {};
};
template<>
struct AstVisitable<void> {
virtual void accept( tag<void>, AstNodeVisitor&v ) = 0;
virtual ~AstVisitable() {};
};
template<class Types>
struct AstVisitables;
template<>
struct AstVisibables<types<>> {
virtual ~AstVisitables() {};
};
template<class T0, class...Ts>
struct AstVisitables<types<T0, Ts...>>:
virtual AstVisitable<T0>,
AstVisitables<types<Ts...>>
{
using AstVisitable<T0>::accept;
using AstVisitables<types<Ts...>>::accept;
};
using supported_ast_return_types = types<int, bool, std::string, void>;
class AstNode:public AstVisitables<supported_ast_return_types> {
public:
void addChild(AstNode* child);
AstNode* left() { return m_left.get(); }
AstNode* right() { return m_right.get(); }
private:
std::unique_ptr<AstNode> m_left;
std::unique_ptr<AstNode> m_right;
};
template<class types>
struct AstVisiablesFailAll;
template<>
struct AstVisiablesFailAll<> {
virtual ~AstVisiablesFailAll() {};
};
template<class T>
struct AstVisitableFailure : virtual AstVisitable<T> {
boost::optional<T> accept( tag<T>, AstNodeVisitor& ) override {
return {};
}
};
template<>
struct AstVisitableFailure<void> : virtual AstVisitable<void> {
void accept( tag<void>, AstNodeVisitor& ) override {
return;
}
};
template<class T0, class...Ts>
struct AstVisitablesFailAll<types<T0, Ts...>>:
AstVisitableFailure<T0>,
AstVisitableFailAll<types<Ts...>>
{
using AstVisitableFailure<T0>::accept;
using AstVisitableFailAll<types<Ts...>>::accept;
};
So now you can boost::optional<bool> lhs = node.left()->accept( tag<bool>, *this );, and from the state of lhs know if the left node can be evaluated in a bool context.
SumNode looks like this:
class SumNode :
public AstNode,
AstVisiablesFailAll<supported_ast_return_types>
{
public:
void accept(tag<void>, AstNodeVisitor& v) override
{
accept(tag<int>, v );
}
boost::optional<int> accept(tag<int>, AstNodeVisitor& v) override
{
return v->visitSumNode(this);
}
int eval(int lhs, int rhs) const {
return lhs + rhs;
}
};
and visitSumNode:
boost::optional<int> visitSumNode(Node& node) {
// won't work, because accept return void!
boost::optional<int> lhs = node.left()->accept(tag<int>, *this);
boost::optional<int> rhs = node.right()->accept(tag<int>, *this);
if (!lhs || !rhs) return {};
return node.eval(*lhs, *rhs);
}
The above assumes that visiting a+b in a void context is acceptable (like in C/C++). If it isn't, then you need a means for void visit to "fail to produce a void".
In short, accepting requires context, which also determines what type you expect. Failure is an empty optional.
The above uses boost::optional -- std::experimental::optional would also work, or you can roll your own, or you can define a poor man's optional:
template<class T>
struct poor_optional {
bool empty = true;
T t;
explicit operator bool() const { return !empty; }
bool operator!() const { return !*this; }
T& operator*() { return t; }
T const& operator*() const { return t; }
// 9 default special member functions:
poor_optional() = default;
poor_optional(poor_optional const&)=default;
poor_optional(poor_optional const&&)=default;
poor_optional(poor_optional &&)=default;
poor_optional(poor_optional &)=default;
poor_optional& operator=(poor_optional const&)=default;
poor_optional& operator=(poor_optional const&&)=default;
poor_optional& operator=(poor_optional &&)=default;
poor_optional& operator=(poor_optional &)=default;
template<class...Ts>
void emplace(Ts&&...ts) {
t = {std::forward<Ts>(ts)...};
empty = false;
}
template<class...Ts>
poor_optional( Ts&&... ts ):empty(false), t(std::forward<Ts>(ts)...) {}
};
which sucks, because it constructs a T even if not needed, but it should sort of work.
For completion sake I post the template version that is mentioned by the OP
#include <string>
#include <iostream>
namespace bodhi
{
template<typename T> class Beignet;
template<typename T> class Cruller;
template<typename T> class IPastryVisitor
{
public:
virtual T visitBeignet(Beignet<T>& beignet) = 0;
virtual T visitCruller(Cruller<T>& cruller) = 0;
};
template<typename T> class Pastry
{
public:
virtual T accept(IPastryVisitor<T>& visitor) = 0;
};
template<typename T> class Beignet : public Pastry<T>
{
public:
T accept(IPastryVisitor<T>& visitor)
{
return visitor.visitBeignet(*this);
}
std::string name = "Beignet";
};
template<typename T> class Cruller : public Pastry<T>
{
public:
T accept(IPastryVisitor<T>& visitor)
{
return visitor.visitCruller(*this);
}
std::string name = "Cruller";
};
class Confectioner : public IPastryVisitor<std::string>
{
public:
virtual std::string visitBeignet(Beignet<std::string>& beignet) override
{
return "I just visited: " + beignet.name;
}
virtual std::string visitCruller(Cruller<std::string>& cruller) override
{
return "I just visited: " + cruller.name;
}
};
}
int main()
{
bodhi::Confectioner pastryChef;
bodhi::Beignet<std::string> beignet;
std::cout << beignet.accept(pastryChef) << "\n";
bodhi::Cruller<std::string> cruller;
std::cout << cruller.accept(pastryChef) << "\n";
return 0;
}
Every pastry is a node and every visitor can implement its accepted return type. Having multiple visitor could visit the same pastry.
In C# / .NET you can do something like this:
someThing.text = "blah";
String blah = someThing.text;
However, the above code does not actually interact with the someThing's text String directly, it uses a get and set property. Similarly, read-only properties can be used.
Is there a way to do something similar in native C++? (not C++ .NET)
WARNING: This is a tongue-in-cheek response and is terrible!!!
Yes, it's sort of possible :)
template<typename T>
class Property
{
private:
T& _value;
public:
Property(T& value) : _value(value)
{
} // eo ctor
Property<T>& operator = (const T& val)
{
_value = val;
return *this;
}; // eo operator =
operator const T&() const
{
return _value;
}; // eo operator ()
};
Then declare your class, declaring properties for your members:
class Test
{
private:
std::string _label;
int _width;
public:
Test() : Label(_label)
, Width(_width)
{
};
Property<std::string> Label;
Property<int> Width;
};
And call C# style!
Test a;
a.Label = "blah";
a.Width = 5;
std::string label = a.Label;
int width = a.Width;
In .NET properties are syntactic sugar for the real get and set functions which are emitted behind the scenes (in fact they are more than syntactic sugar because properties are emitted in the resulting IL and could be used with Reflection). So in C++ you would need to explicitly write those functions as there's no such notion as property.
I warn you, it is not fully compatible native C++: Microsoft-specific C++ only.
The Microsoft compiler allows you to use declspec(property), this way:
struct S {
int i;
void putprop(int j) {
i = j;
}
int getprop() {
return i;
}
// here you define the property and the functions to call for it
__declspec(property(get = getprop, put = putprop)) int the_prop;
};
int main() {
S s;
s.the_prop = 5; // THERE YOU GO
return s.the_prop;
}
cf Microsoft Documentation for more details: declspec(property).
Moo-Juice's answer looks really cool, but has a drawback: you can't use these properties like normal expressions of type T, as you can in C#.
For instance,
a.text.c_str() won't compile (‘class Property<std::basic_string<char> >’ has no member named ‘c_str’)
std::cout << a.text won't compile either (template argument deduction/substitution failed)
I would suggest the following enhancement to template<typename T> class Property:
T& operator() ()
{
return _value;
}
T const& operator() () const
{
return _value;
}
Then you can access the property's members with (), such as:
char const *p = a.text().c_str();
And you can use the property in expressions where the type must be deduced:
std::cout << a.text();
A property in .NET is associated with a get and/or a set member function, so it's really just syntactic sugar. The closest you can get with C++ is to use overloading to give the getter and setter the same name:
const std::string &test() const { return text_; }
void test(const std::string &value) { text_ = value; }
Obviously, you will still have to provide parenthesis for the call:
someThing.text("blah");
String blah = someThing.text();
Yes but it's vendor specific. Microsoft has declspec(property).
C++Builder's implementation is a bit more advanced (via vendor specific __property keyword) in that you could have indexed accessors (which can be of any types you wish).
Also check this out (without relying on vendor specific keywords): http://www.codeproject.com/KB/cpp/cpp_property_indexer.aspx
#include <iostream>
#include <string>
using namespace std;
// ------------------------------------------------------------------
#define PROPERTY_GET_SET(CLASS, NAME, TYPE) GetSetProperty<CLASS, TYPE> NAME() { return GetSetProperty<CLASS, TYPE>(this, &CLASS::get_##NAME, &CLASS::set_##NAME); }
#define PROPERTY_GET(CLASS, NAME, TYPE) GetProperty<CLASS, TYPE> NAME() { return GetProperty<CLASS, TYPE>(this, &CLASS::get_##NAME); }
#define PROPERTY_SET(CLASS, NAME, TYPE) SetProperty<CLASS, TYPE> NAME() { return SetProperty<CLASS, TYPE>(this, &CLASS::set_##NAME); }
template <typename CLASS, typename TYPE>
struct GetSetProperty {
typedef TYPE (CLASS::*Getter_t)() const;
typedef void (CLASS::*Setter_t)(TYPE);
GetSetProperty(CLASS* instance, Getter_t getter, Setter_t setter) : m_instance(instance), m_getter(getter), m_setter(setter) {}
operator TYPE() const { return (this->m_instance->*this->m_getter)(); }
GetSetProperty<CLASS, TYPE>& operator=(TYPE value) { (this->m_instance->*this->m_setter)(value); return *this; }
CLASS* const m_instance;
const Getter_t m_getter;
const Setter_t m_setter;
};
template <typename CLASS, typename TYPE>
struct GetProperty {
typedef TYPE (CLASS::*Getter_t)() const;
GetProperty(CLASS* instance, Getter_t getter) : m_instance(instance), m_getter(getter) {}
operator TYPE() const { return (this->m_instance->*this->m_getter)(); }
CLASS* const m_instance;
const Getter_t m_getter;
};
template <typename CLASS, typename TYPE>
struct SetProperty {
typedef void (CLASS::*Setter_t)(TYPE);
SetProperty(CLASS* instance, Setter_t setter) : m_instance(instance), m_setter(setter) {}
SetProperty<CLASS, TYPE>& operator=(TYPE value) { (this->m_instance->*this->m_setter)(value); return *this; }
CLASS* const m_instance;
const Setter_t m_setter;
};
template <typename CLASS, typename TYPE>
ostream& operator<<(ostream& ostr, const GetSetProperty<CLASS, TYPE>& p) { ostr << (p.m_instance->*p.m_getter)(); return ostr; }
template <typename CLASS, typename TYPE>
ostream& operator<<(ostream& ostr, const GetProperty<CLASS, TYPE>& p) { ostr << (p.m_instance->*p.m_getter)(); return ostr; }
// ------------------------------------------------------------------
class Dummy
{
public:
Dummy() : m_value1(42) {}
PROPERTY_GET_SET(Dummy, Value1, int);
PROPERTY_GET_SET(Dummy, Value2, const string&);
protected:
virtual int get_Value1() const { return this->m_value1; }
virtual void set_Value1(int value) { this->m_value1 = value; }
virtual const string& get_Value2() const { return this->m_value2; }
virtual void set_Value2(const string& value) { this->m_value2 = value; }
private:
int m_value1;
string m_value2;
};
int main(int argc, char* argv[]) {
Dummy d;
cout << d.Value1() << endl;
d.Value1() = 3;
cout << d.Value1() << endl;
cout << d.Value2() << endl;
d.Value2() = "test";
cout << d.Value2() << endl;
return 0;
}
// ------------------------------------------------------------------
By using std::function you can get pretty close. Featurewise everything is here.
First create the templated Property class:
#include <functional>
template<class T>
class Property
{
std::function<T (void)> _get;
std::function<void(const T&)> _set;
public:
Property(
std::function<T (void)> get,
std::function<void(const T&)> set)
: _get(get),
_set(set)
{ }
Property(
std::function<T(void)> get)
: _get(get),
_set([](const unsigned int&){})
{ }
operator T () const { return _get(); }
void operator = (const T& t) { _set(t); }
};
Use the Property in a class by creating a get and a set method similar to what you would in do C#:
class Test
{
private:
std::string _label;
public:
Property<std::string> Label = Property<std::string>
(
[this]()->std::string
{
return this->_label;
},
[this](const std::string& value)
{
this->_label = value;
}
);
Property<unsigned int> LabelSize = Property<unsigned int>
(
[this]()->unsigned int
{
return this->_label.size();
}
);
};
Testing this code:
Test test;
test.Label = "std functional";
std::cout << "label = " << std::string(test.Label) << std::endl
<< "label size = " << int(test.LabelSize) << std::endl;
will output
label = std functional
label size = 14
I think this is as syntactic-sugar-coated as you can get it in c++ :)
Probably the best option currently is to use the microsoft's __declspec( property( get=get_func_name, put=put_func_name ) ) PropertyType PropertyName attribute.
it is also supported by clang,
it is converted into your getter/setter when compiled (won't add any new variables),
in use, it is the closest thing to a real property (can access property of a property...).
But if you're using other compilers, you could use macros:
#define PROPERTY_GEN(Class, Type, Name, GetMethod, SetMethod) \
class Property_##Name { \
public: \
Property_##Name(Class* parent) : _parent(parent) { } \
Type operator = (Type value) \
{ \
_parent->SetMethod(value); \
return _parent->GetMethod(); \
} \
operator Type() const \
{ \
return static_cast<const Class*>(_parent)->GetMethod(); \
} \
Property_##Name& operator =(const Property_##Name& other) \
{ \
operator=(other._parent->GetMethod()); return *this; \
}; \
Property_##Name(const Property_##Name& other) = delete; \
private: \
Class* _parent; \
} Name { this };
// PROPERTY - Declares a property with the default getter/setter method names.
#define PROPERTY(Class, Type, Name) \
PROPERTY_GEN(Class, Type, Name, get_##Name, set_##Name)
Then use them like:
class SomeClass
{
public:
PROPERTY(SomeClass, int, Value)
int get_Value() const { return _value; }
void set_Value(int value) { _value = value; }
private:
int _value = 0;
};
int main()
{
SomeClass s, c;
s.Value = 5;
c.Value = 3 * s.Value;
s.Value = c.Value;
}
You could also add other macro variants for read-only, write-only properties and read-only non-const getters. To be able to access sub-properties via ->, you could add operator-> overloads to the macro.
Compared to microsoft's __declspec(property(...)), getter and setter methods can be made private but this isn't a real advantage since client might need to take the address of a getter/setter sometimes.
There is also a disadvantage of having an additional _parent variable for every property, and you would need to explicitly define copy constructors for parent classes if they are used.
I realize it's this question is probably too old to add another answer but to expand on Moo-Juice's answer, I've come up with a pretty neat and simple solution:
/// Utility for functions get, set & ptr.
template<typename TVal>
using GetFn = std::function<const TVal& (void)>;
template<typename TVal>
using SetFn = std::function<void(const TVal&)>;
template<typename TVal>
using PtrFn = std::function<TVal* (void)>;
/// The property class and each specialization utility.
template<typename TVal, bool Delegate, bool ReadOnly>
class Property;
template<typename TVal>
using PropertyGetSet = Property<TVal, false, false>;
template<typename TVal>
using PropertyDelGetSet = Property<TVal, true, false>;
template<typename TVal>
using PropertyGet = Property<TVal, false, true>;
template<typename TVal>
using PropertyDelGet = Property<TVal, true, true>;
/// <summary>
/// Property get-set.
/// </summary>
/// <typeparam name="TVal">Value type.</typeparam>
template<typename TVal>
class Property<TVal, false, false>
{
public:
typedef TVal Value;
Property(const TVal& val)
: m_value(val)
{}
inline const TVal& Get() const { return m_value; }
inline void Set(const TVal& val) { m_value = val; }
inline TVal* Ptr() { return &m_value; }
private:
TVal m_value;
};
/// <summary>
/// Property delegate get-set.
/// </summary>
/// <typeparam name="TVal">Value type.</typeparam>
template<typename TVal>
class Property<TVal, true, false>
{
public:
typedef TVal Value;
Property(GetFn<TVal> getFn, SetFn<TVal> setFn, PtrFn<TVal> ptrFn)
: m_getFn(getFn)
, m_setFn(setFn)
, m_ptrFn(ptrFn)
{}
inline const TVal& Get() const { return m_getFn(); }
inline void Set(const TVal& val) { m_setFn(val); }
inline TVal* Ptr() { return m_ptrFn(); }
private:
GetFn<TVal> m_getFn;
SetFn<TVal> m_setFn;
PtrFn<TVal> m_ptrFn;
};
/// <summary>
/// Property get.
/// </summary>
/// <typeparam name="TVal">Value type.</typeparam>
template<typename TVal>
class Property<TVal, false, true>
{
public:
typedef TVal Value;
Property(const TVal& val)
: m_value(val)
{}
inline const TVal& Get() const { return m_value; }
inline TVal* Ptr() { return &m_value; }
private:
TVal m_value;
};
/// <summary>
/// Property delegate get.
/// </summary>
/// <typeparam name="TVal">Value type.</typeparam>
template<typename TVal>
class Property<TVal, true, true>
{
public:
typedef TVal Value;
Property(GetFn<TVal> getFn, PtrFn<TVal> ptrFn)
: m_getFn(getFn)
, m_ptrFn(ptrFn)
{}
inline const TVal& Get() const { return m_getFn(); }
inline TVal* Ptr() { return m_ptrFn(); }
private:
GetFn<TVal> m_getFn;
PtrFn<TVal> m_ptrFn;
};
And then to use it:
PropertyGetSet<std::string> strGetSet = PropertyGetSet<std::string>("GetSet");
std::string m_strGetSet = "DelGetSet";
PropertyDelGetSet<std::string> strDelGetSet =
PropertyDelGetSet<std::string>(
[&]() -> const std::string& { return m_strGetSet; },
[&](const std::string& val) { m_strGetSet = val; },
[&]() { return &m_strGetSet; /* throw? */ });
// The get (read-only) version is the same but without the set function
Some caveats:
The get function returns a const& so you are not able to use it for changing the value, this is by design as it would allow people to use the reference to set the value instead of the explicit Set which gives the advantage of knowing when the value is set.
There is no syntactic sugar for the get-set-ptr functions, personally, I didn't like using operators cause it made the underlying system more obtuse, so using explicit functions lets the user know that it's a property and not something else. But if you may, you could sprinkle some operator overloads.
All specializations have a Ptr function which will be the pointer of the data. However, when using the delegate version, you can choose to throw so anyone trying to use it will have to work around it. The reason it's there is that in the worst-case scenario you may try to use the pointer for a very particular situation, I would highly advise not to use this tho, so feel free to remove it or make an extra specialization for it.
Lastly, it's a bit verbose, you could wrap the usage in a macro to make the syntax a bit shorter, but personally, I like it the way it is as it's more explicit that way.
EDIT:
You may run into the same issue I had with this design, have a look at the following link for the issue and the solution I've come up with: https://stackoverflow.com/a/68563492/3339838
Another try to enhance Moo-Juice's answer, by defining a Getter class (which the client can only get) and a Getter-Setter class which is also assignable:
template <typename T>
class Getter {
protected:
T &_value;
public:
Getter(T &value) : _value(value) {}
operator const T() const {
return _value;
}
};
template <typename T>
class GetterSetter : public Getter<T> {
using Getter<T>::_value;
using Getter<T>::Getter;
public:
GetterSetter<T> & operator=(const T val) {
_value = val;
return *(this);
}
};
This gives you the option to decide which properties could be changed from outside the class, and which will only change internally.
No, there is not. You would just create getter and setter functions:
someThing.setText("blah");
std::string blah = someThing.getText();