My problem is very specific. I have the following requirement, I need to set member variables that exist in child class from parent class, for several reasons. My plan is to pass a function pointer of the setter method (which exist in child class), that takes string as argument, to the parent class at construction. The parent class defines a public method that takes member name and value string as argument and invokes the function pointer of the member with value string. The parent class can exist in a dll or lib and have no access to any conversion or factory methods, so the setter method have to be defined in child class.
Since the parent can be a base class for other classes, i wrote some macros shown as below:
#define DEFINE_VAL(Type, memberName) \
private: \
Type memberName; \
void set##memberName(std::string const& val) { \
memberName = convert_to_val(val); /* this will be a call to factory which converts string to value type*/\
/* or call to local implementation for conversion*/
}; \
#define INIT_VAL(memberName) \
{ memberName, \
[&](std::string const& val) { set##memberName(val); }}
Parent and child classes are as below:
// parent.h probably in dll
class parent
{
public:
parent(std::map<std::string, std::function<void(std::string const&)>>& m)
: m(m)
{ }
...
private:
std::map<std::string, std::function<void(std::string const&)>> m;
};
// child.h
class child : public parent
{
public:
child() : parent({ INIT_VAL(iVal), ... })
{ }
private:
DEFINE_VAL(int, iVal);
...
};
The child class can have many variables defined and its a bit annoying to first use DEFINE_VAL macro and then pass each variable's setter method with INIT_VAL macro. Can this be done in one macro (probably in DEFINE_VAL)? or any ideas on automatic registration of member name and function pointer to parent class?
I would also appreciate any alternative ideas on accomplishing my requirement.
I need to set member variables that exist in child class from parent class, for several reasons. My plan is to pass a function pointer of the setter method (which exist in child class), that takes string as argument, to the parent class at construction.
When parent class constructor is invoked the derived class and its members have not been initialized yet and, pedantically, they do not exist yet. For this reason, it is not possible to set derived class members from its base class constructor.
One solution is to use a virtual function to set members by name.
Without built-in reflection in current C++, to associate names with data members and generate member accessors the best practice is still to use macros. One of the best macros for this purpose is BOOST_HANA_DEFINE_STRUCT.
boost::lexical_cast<T> can be used to convert from std::string to any T.
A working example with deep and multiple inheritance support:
#include <boost/hana/define_struct.hpp>
#include <boost/hana/accessors.hpp>
#include <boost/hana/for_each.hpp>
#include <boost/hana/concat.hpp>
#include <boost/hana/length.hpp>
#include <boost/lexical_cast.hpp>
#include <unordered_map>
#include <functional>
#include <iostream>
namespace hana = boost::hana;
struct MemberSetter {
// Using void* to reduce the number of template instantiations.
using SetterFn = std::function<void(void*, std::string const&)>;
using Setters = std::unordered_map<std::string, SetterFn>;
Setters setters_;
template<class Derived, class Accessors>
MemberSetter(Derived* that, Accessors& accessors) {
hana::for_each(accessors, [this](auto const& pair) {
auto setter = [accessor = hana::second(pair)](void* vthat, std::string const& value) {
auto* that = static_cast<Derived*>(vthat);
auto& member = accessor(*that);
member = boost::lexical_cast<std::remove_reference_t<decltype(member)>>(value);
};
auto name = hana::first(pair);
setters_.emplace(std::string(hana::to<char const*>(name), hana::length(name)), std::move(setter));
});
}
bool findAndSetMember(void* that, std::string const& name, std::string const& value) const {
auto setter = setters_.find(name);
if(setter != setters_.end()) {
(setter->second)(that, value);
return true;
}
return false;
}
};
struct A {
virtual ~A() = default;
virtual bool setMember(std::string const& name, std::string const& value) = 0;
};
struct B : A {
BOOST_HANA_DEFINE_STRUCT(B,
(int, a),
(double, b)
);
bool setMember(std::string const& name, std::string const& value) override {
constexpr auto accessors = hana::accessors<B>();
static MemberSetter const setter(this, accessors);
return setter.findAndSetMember(this, name, value);
}
};
struct C : B {
BOOST_HANA_DEFINE_STRUCT(C,
(std::string, c)
);
bool setMember(std::string const& name, std::string const& value) override {
constexpr auto accessors = hana::concat(hana::accessors<B>(), hana::accessors<C>()); // Join with members of the base class.
static MemberSetter const setter(this, accessors);
return setter.findAndSetMember(this, name, value);
}
};
int main() {
C c;
c.setMember("a", "1");
c.setMember("b", "2.3");
c.setMember("c", "hello");
std::cout << c.a << ' ' << c.b << ' ' << c.c << '\n';
}
Output:
1 2.3 hello
Just use a virtual function to set it, and move the map to the child as it really should be an implementation detail. This way the parent class doesn't really have anything to do with how the members are set.
class parent
{
public:
virtual ~parent() = default;
protected:
virtual void do_set(const std::string& name, const std::string& value) = 0;
private:
void set(const std::string& name, const std::string& value) {
do_set(name, value);
// Do synchronization here
}
};
class child : public parent
{
protected:
void do_set(const std::string& name, const std::string& value) override {
child::setter_map.at(name)(*this, value);
}
private:
int iVal;
static const std::map<std::string, void(*)(child&, const std::string&)> setter_map;
};
#define INIT_VAL(NAME, ...) { #NAME, [](child& c, const std::string& value) __VA_ARGS__ }
const std::map<std::string, void(*)(child&, const std::string&)> child::setter_map = {
INIT_VAL(iVal, {
c.iVal = convert_to_val(value);
}),
// Init other members
};
And from this, you might be able to find a better way to implement set (Maybe a simple if (name == ...) ... else if (name == ...) ... would work)
Or if you don't want to use runtime polymorphism, at least don't store a map in every instance of parent. Store a reference to a global map (Which would be like a vtable itself):
class parent
{
public:
parent() = delete;
protected:
using setter_map = std::map<std::string, void(*)(parent&, const std::string&)>;
parent(const setter_map& child_smap) noexcept : smap(&child_smap) {};
private:
void set(const std::string& name, const std::string& value) {
smap->at(name)(*this, value);
// Do synchronization here
}
const setter_map* smap;
};
class child : public parent {
public:
child() : parent(smap) {};
private:
int iVal;
static const setter_map smap;
};
#define INIT_VAL(NAME, ...) { #NAME, \
[](parent& _c, const std::string& value) { \
child& c = static_cast<child&>(_c); \
__VA_ARGS__ \
} \
}
const child::setter_map child::smap = {
INIT_VAL(iVal, {
c.iVal = convert_to_val(value);
}),
// (Other member setters here)
};
#undef INIT_VAL
// Or having the setters inside the class, like in your original code
class child2 : public parent {
public:
child2() : parent(smap) {};
private:
int iVal;
void set_iVal(const std::string& value) {
iVal = convert_to_val(value);
}
// Using a macro (Probably don't need the macros here, writing out a setter is more clear)
template<class T>
using type = T;
#define DEFINE_VAL(TYPE, NAME, ...) \
void set_ ## NAME (const std::string& value) { \
__VA_ARGS__ \
} \
type<TYPE> NAME
DEFINE_VAL(float, fVal, {
fVal = convert_val_to_float(value);
});
DEFINE_VAL(char[2], charArrVal, {
charArrVal[0] = value[0];
charArrVal[1] = value[1];
});
static const setter_map smap;
};
#define INIT_VAL(NAME) { #NAME, [](parent& p, const std::string& value) { static_cast<child2&>(p).set_ ## NAME (value); } }
const child2::setter_map child2::smap = {
INIT_VAL(iVal), INIT_VAL(fVal), INIT_VAL(charArrVal)
};
#undef INIT_VAL
// Or if `convert_to_val(value)` is literally the body of every setter, that simplifies the `INIT_VAL` macro
class child3 : public parent {
public:
child3() : parent(smap) {};
private:
int iVal;
static const setter_map smap;
};
#define INIT_VAL(NAME) { #NAME, [](parent& p, const std::string& value) { static_cast<child3&>(p). NAME = convert_to_val(value); } }
const child3::setter_map child3::smap = {
INIT_VAL(iVal)
};
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
Suppose I have a class in C++11 like this:
class Something
{
...
private:
class1* a;
class2* b;
class3* c;
public:
class1* reada() { return a; }
class2* readb() { return b; }
class3* readc() { return c; }
void customFunctionForclass1();
void customFunctionForclass2();
void customFunctionForclass3();
}
}
I'd like to make the read functions templated so that if another programmer adds another member class, the corresponding read function will be template-magic created.
Something like this maybe?
class Something
{
...
private:
templateContainer = {class1*,class2*,class3*}
template<thing in templateContainer>
thing variableOfTypeThing;
public:
template<thing in templateContainer>
<thing> read() {return variableOfTypeThing<thing>;}
void customFunctionForclass1();
void customFunctionForclass2();
void customFunctionForclass3();
}
As you can tell from the example, I'm confused.
Basically, I have a class which acts as a container for guaranteed unique class variables (no class1 A; class1 B)
Some function groups for the class are almost identical some function groups are highly varied. It would be great for future people to only have to modify the different parts of the class and get the rest from the templates.
I thought maybe there would be a way by splitting this class up into lots of classes and stuffing them into an array of void pointers, but that seems unwise.
Suggestions?
I'd like to make the read functions templated so that if another programmer adds another member class, the corresponding read function will be template-magic created.
You could encapsulate the user defined classes in a thin wrapper class with a read() function that returns the contained instance. Adding a user defined class to Something would then be done by inheriting wrapper<user_defined_class>.
Basically, I have a class which acts as a container for guaranteed unique class variables
Inheriting this wrapper prevents you from including the same class twice so it could possibly be a way forward:
#include <iostream>
// the "thing" wrapper
template<typename T>
struct thing {
// forward construction arguments to the contained variable
template<class... Args>
thing(Args&&... args) : variable(std::forward<Args>(args)...) {}
// basic interface, const and non-const. I called it get() instead of read()
T const& get() const { return variable; }
T& get() { return variable; }
private:
T variable;
};
// a troublesome user defined class that is not default constructibe :-(
struct user_defined {
user_defined() = delete; // silly example really, but it's just to demonstrate
user_defined(const std::string& v) : str(v) {}
user_defined& operator=(const std::string& v) {
str = v;
return *this;
}
std::string const& say() const { return str; }
private:
std::string str;
};
std::ostream& operator<<(std::ostream& os, const user_defined& ud) {
return os << ud.say();
}
// ... and the "Something" class that inherits the wrapped types.
class Something : thing<int>,
thing<double>,
thing<user_defined>
{
public:
// add initial values for types that are not default constructible
Something(const std::string& val) : thing<user_defined>(val) {}
Something() : Something("") {} // default ctor
// access via derived class, const and non-const
template<typename T>
T const& get() const {
return thing<T>::get(); // get() from the correct base
}
template<typename T>
T& get() {
return thing<T>::get(); // get() from the correct base
}
};
void print(const Something& s) {
// using the const interface
std::cout << s.get<int>() << "\n";
std::cout << s.get<double>() << "\n";
std::cout << s.get<user_defined>() << "\n";
}
int main() {
Something foo;
// using the non-const interface to set
foo.get<int>() = 10;
foo.get<double>() = 3.14159;
foo.get<user_defined>() = "Hello world";
print(foo);
}
Edit: It doesn't fulfill the index part of your question though. You access it using the type you'd like to get() as a tag. You basically build a very rudimentary tuple I guess.
Code based on #Ted Lyngmo's answer:
#include <iostream>
#include <string>
template<typename T>
struct thing {
// forward construction arguments to the contained variable
template<class... Args>
thing(Args&&... args) : variable(std::forward<Args>(args)...) {}
// basic interface, const and non-const. I called it get() instead of read()
T const& get() const { return variable; }
T& get() { return variable; }
protected:
T variable;
};
template<typename ...Ts>
struct things : thing<Ts>... {
template<class... SubTs>
things(thing<SubTs>&&... ts) : thing<SubTs>(std::move(ts))... {}
// access via derived class, const and non-const
template<typename T>
T const& get() const {
return thing<T>::get(); // get() from the correct base
}
template<typename T>
T& get() {
return thing<T>::get(); // get() from the correct base
}
};
// a troublesome user defined class that is not default constructibe :-(
struct user_defined {
user_defined() = delete; // silly example really, but it's just to demonstrate
user_defined(const std::string& v) : str(v) {}
user_defined& operator=(const std::string& v) {
str = v;
return *this;
}
std::string const& say() const { return str; }
private:
std::string str;
};
struct non_default {
non_default() = delete;
non_default(int) {}
};
std::ostream& operator<<(std::ostream& os, const user_defined& ud) {
return os << ud.say();
}
// ... and the "Something" class that inherits the wrapped types.
class Something : public things<int, double, user_defined, non_default>
{
public:
// add initial values for types that are not default constructible
Something(const std::string& val) : things(thing<user_defined>(val), thing<non_default>(0)) {}
Something() : Something("") {} // default ctor
};
void print(const Something& s) {
// using the const interface
std::cout << s.get<int>() << "\n";
std::cout << s.get<double>() << "\n";
std::cout << s.get<user_defined>() << "\n";
}
int main() {
Something foo;
// using the non-const interface to set
foo.get<int>() = 10;
foo.get<double>() = 3.14159;
foo.get<user_defined>() = "Hello world";
print(foo);
}
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();
Hi i am trying to create a simple ORM in c++ for a project. For this example assuming a simple class as
class userProfile: public BaseOrm
{
public:
string username;
string email;
};
Now base orm has a method save() and migrate(). What i want is when a person calls migrate() all the schema , in this case username and email are populated as db tables and on save they persist on database.
What i am having problem with is how do i get what all fields are defined in the class, like in this example username and email and also there types, string in this case. Any help would be appreciated.
I know there is no reflection in c++, so i don't actually care about the variable name but more on the number of variables and there types to map them with DB.
adding reflection to c++ is not insanely difficult but it does require a reasonably good knowledge of template type deduction and some careful planning.
In this working example I have made a start for you. This framework supports writing the members out to a "statement" class (modelling a database prepared statement).
Similar techniques can be used to build out the SQL generation for CRUD.
No doubt there are already libraries that do this for you...
#include <iostream>
#include <iomanip>
#include <string>
#include <tuple>
#include <utility>
using namespace std;
struct statement
{
void setString(int index, const std::string& value)
{
std::cout << "setting index " << index << " to value " << std::quoted(value) << std::endl;
}
};
struct BaseOrm
{
virtual void serialise(statement& stmt) const = 0;
};
template<class Class>
struct class_tag {
using type = Class;
};
template<const char* Name>
struct name_tag {
static constexpr const char* name() { return Name; }
};
namespace detail {
struct reflection_item_concept
{
virtual const std::string& name() const = 0;
virtual std::string to_archive_string(const void* object) const = 0;
virtual void from_archive_string(void* object, const std::string& as) const = 0;
};
template<class T>
std::string to_archive_string_impl(const T& val) {
return std::to_string(val);
}
const std::string& to_archive_string_impl(const std::string& s) {
return s;
}
template<class NameTag, class Class, class Type>
struct reflection_item : reflection_item_concept
{
reflection_item(Type Class::* mfp) : mfp(mfp) {}
static const class_tag<Class> class_info() { return {}; };
static const char* raw_name() { return NameTag::name(); };
// concept implementation
const std::string& name() const override {
static const std::string s = raw_name();
return s;
}
std::string to_archive_string(const void* object) const override
{
auto& val = (*reinterpret_cast<const Class*>(object)).*mfp;
return to_archive_string_impl(val);
}
void from_archive_string(void* item, const std::string& as) const override
{
// similar mechanism here
}
Type Class::* mfp;
};
}
template<class NameTag, class Class, class Type>
constexpr auto reflection_item(NameTag, Type Class::* mp)
{
return detail::reflection_item<NameTag, Class, Type> { mp };
}
struct class_reflection_concept
{
virtual void serialise(const void* object, statement& stmt) const = 0;
};
namespace detail {
template<class ClassTag, class...ReflectionItems>
struct reflection_impl : class_reflection_concept
{
reflection_impl(ReflectionItems...refs)
: _reflectors(std::make_tuple(refs...))
{}
template<std::size_t...Is>
void serialise_impl(std::index_sequence<Is...>, const void* object,
statement& stmt) const
{
using expand = int[];
void(expand{
0,
(stmt.setString(Is + 1, std::get<Is>(_reflectors).to_archive_string(object)),0)...
});
}
void serialise(const void* object, statement& stmt) const override
{
serialise_impl(std::make_index_sequence<sizeof...(ReflectionItems)>(),
object, stmt);
}
std::tuple<ReflectionItems...> _reflectors;
};
}
template<class ClassTag, class...ReflectionItems>
auto& make_reflection(ClassTag tag, ReflectionItems...items)
{
static const detail::reflection_impl<ClassTag, ReflectionItems...> _ { items... };
return _;
}
const char txt_username[] = "username";
const char txt_email[] = "email";
const char txt_x[] = "x";
class userProfile: public BaseOrm
{
public:
string username = "test username";
string email = "noone#nowhere.com";
int x = 10;
// implement serialisation
void serialise(statement& stmt) const override
{
reflection.serialise(this, stmt);
}
static const class_reflection_concept& reflection;
};
const class_reflection_concept& userProfile::reflection =
make_reflection(class_tag<userProfile>(),
reflection_item(name_tag<txt_username>(), &userProfile::username),
reflection_item(name_tag<txt_email>(), &userProfile::email),
reflection_item(name_tag<txt_x>(), &userProfile::x));
int main()
{
userProfile x;
statement stmt;
x.serialise(stmt);
}
expected results:
setting index 1 to value "test username"
setting index 2 to value "noone#nowhere.com"
setting index 3 to value "10"
What I understand is that you want a generic behaviour for classes which have a variable set of fields.
I suggest you to create a "field" interface which will be stored in your base class with a container (for example a map of [fieldName, fieldInterface]). You still have to implement a behaviour for each field's type, but then you can create any class derived from the base class which have a dynamic set of field.
Here is an example :
#include <iostream>
#include <map>
using namespace std;
//the "Field" interface
class IFieldOrm
{
public:
virtual ~IFieldOrm() {}
virtual void save() = 0;
virtual void migrate() = 0;
};
//your base class
class BaseOrm
{
public:
virtual ~BaseOrm();
virtual void save();
virtual void migrate();
protected:
map<string, IFieldOrm*> m_fields; //prefer a smart pointer if you don't want to mess with raw pointer
};
//base class implementation
void BaseOrm::save()
{
for(auto& f : m_fields)
f.second->save();
}
void BaseOrm::migrate()
{
for(auto& f : m_fields)
f.second->migrate();
}
//don't forget to free your "fields" pointers if you have raw pointers
BaseOrm::~BaseOrm()
{
for(auto& f : m_fields)
delete f.second;
}
//then implement your basic types
//(like string, int, ..., whatever type you want to store in your database)
class StringFieldOrm : public IFieldOrm
{
public:
StringFieldOrm(const string& value) : m_value(value) {}
virtual void save();
virtual void migrate();
private:
string m_value;
};
void StringFieldOrm::save()
{
cout << "Save value " << m_value << endl;
//save stuff...
}
void StringFieldOrm::migrate()
{
cout << "Migrate value " << m_value << endl;
//migrate stuff...
}
class IntFieldOrm : public IFieldOrm
{
public:
IntFieldOrm(int& value) : m_value(value) {}
virtual void save();
virtual void migrate();
private:
int m_value;
};
void IntFieldOrm::save()
{
cout << "Save value " << m_value << endl;
//save stuff...
}
void IntFieldOrm::migrate()
{
cout << "Migrate value " << m_value << endl;
//migrate stuff
}
//and finally implement your final class
//note that this object can be "dynamically extended" by inserting new fields,
//you may want to prevent that and I can think of a solution if you want to
class UserProfile: public BaseOrm
{
public:
UserProfile(const string& username, const string& email, int age);
};
UserProfile::UserProfile(const string& username, const string& email, int age)
{
m_fields["username"] = new StringFieldOrm(username);
m_fields["email"] = new StringFieldOrm(email);
m_fields["age"] = new IntFieldOrm(age);
}
int main(int argc, char* argv[])
{
UserProfile user = UserProfile("Batman", "bw#batmail.com", 30);
user.save();
return 0;
}
create a userProfile variable and access them:
userProfile user;
int main(){
std::cout << user.username;
std::cout << user.email ;
}
this is how you would access them, except for different reasons, not printing them to the screen.
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();