https://github.com/mousebyte/memprop
Member property implementation suitable for use in UI libraries.
https://github.com/mousebyte/memprop
cpp cpp20 properties
Last synced: about 12 hours ago
JSON representation
Member property implementation suitable for use in UI libraries.
- Host: GitHub
- URL: https://github.com/mousebyte/memprop
- Owner: mousebyte
- License: mit
- Created: 2021-01-25T20:48:58.000Z (over 5 years ago)
- Default Branch: main
- Last Pushed: 2021-01-30T21:52:18.000Z (over 5 years ago)
- Last Synced: 2025-02-25T04:13:07.808Z (over 1 year ago)
- Topics: cpp, cpp20, properties
- Language: C++
- Homepage:
- Size: 48.8 KB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# memprop
Memprop is a member property implementation suitable for use in UI libraries. Features change notifications, access control, and bindings. Requires a compiler with C++20 Concepts support.
## Installation
Memprop is a header only library. It depends on [mousebyte/sigslot20](https://github.com/mousebyte/sigslot20) for property change notifications. As long as you have `sigslot/signal.hpp` available in your project, all you need is the Memprop include directory.
### Install with CMake
You can install Memprop either as a subdirectory/submodule of your project, or to the system using the install target.
First, clone the Memprop repo using `git clone` or `git submodule add`. Then update memprop's submodules. If you're not interested in running the test cases, the only one you'll need is sigslot.
```
cd memprop
git submodule update --init ./lib/sigslot20
```
If you're using memprop as a subdirectory of your project, just add it to your `CMakeLists.txt`:
```cmake
option(MEMPROP_COMPILE_TESTS "" OFF)
add_subdirectory(memprop)
add_executable(MyExe main.cpp)
target_link_libraries(MyExe PRIVATE Mousebyte::memprop)
```
Otherwise, you can install it wherever you like.
```
mkdir build
cd build
cmake .. -B. -DMEMPROP_COMPILE_TESTS=OFF -DCMAKE_INSTALL_PREFIX=/path/to/install
cmake --build . --target install
```
## Usage
### Public properties
There are a number of different property types to choose from based on use case. The simplest property type is `public_property`, which any caller can get or set. The first template argument to any property is always the type of the owning class. Getters and setters are provided as member function pointers.
```c++
class foo {
bool set_StringProp(std::string& out, std::string const& in)
{
out = in.substr(0, 32); // perform some custom processing or validation
return true; // return false to suppress Changed signal
}
public:
// Public property with default setter and getter
memprop::public_property IntProp {this};
// Public property with custom setter
memprop::public_property StringProp;
foo() : StringProp(this, "bar") // Non-backed property constructors can accept an initial value
{
}
};
```
### Readonly properties
Readonly properties are identical to public properties, but can only be set from within the owner type.
```c++
class foo {
public:
memprop::readonly_property RoProp {this};
void bar()
{
RoProp = 17; // Can be set from inside foo
}
};
void baz(foo& f)
{
int i = f.RoProp; // Can read the value from outside
//f.RoProp = 22; Error: produces an access violation
}
```
### Backed properties
Both public and readonly properties have variants which use a backing field. These properties require a custom getter and setter.
```c++
class foo {
int _anInt;
int const& get_int() const // Getter returns a const reference
{
return _anInt;
}
bool set_int(int const& v) // Setter for backed property takes a const reference
{
if(v <= 100) {
_anInt = v;
return true;
}
return false;
}
public:
memprop::backed_public_property BackedProp {this};
};
```
### Computed properties
The last type of property is the `computed_property`, which has a custom getter and no setter whatsoever.
```c++
class foo {
float magic_number() const
{
//do some magic
}
public:
memprop::computed_property ComputedProp {this};
};
```
### Change notifications
All property types except `computed_property` have a member signal, `Changed`, which is invoked each time the property's value is set from the property object. A const reference to the property's new value is passed to each slot. For more information on the signals used in this library, check out the [sigslot20](https://github.com/mousebyte/sigslot20) repo.
### Property binding
A property can be bound to the value of another property with the `bind()` member function. Readonly properties can only be bound to the value of another property from within their owner class. The only property type which does not support binding is `computed_property`.
A custom converter object can be passed to `bind()`. Converters must have an `operator()` which expects a const reference to the source value type, and returns the target value type.
```c++
#include
class foo {
public:
memprop::public_property FooIntProp {this};
memprop::public_property FooStringProp {this};
};
class bar {
public:
memprop::public_property BarIntProp {this};
memprop::readonly_property BarReadonlyIntProp {this};
};
struct custom_converter {
std::string operator()(int const& v)
{
return std::to_string(v);
}
};
void do_some_binding()
{
foo f;
bar b;
// FooIntProp will now be set whenever BarReadonlyIntProp is set
auto binding = f.FooIntProp.bind(b.BarReadonlyIntProp);
//b.BarReadonlyIntProp.bind(f.FooIntProp); Error: can't bind to readonly property outside of bar
// Bindings can be managed through the handle returned by bind()
binding->disconnect();
// Bindings can be bi-directional, setting either property will update the other
f.FooIntProp.bind(b.BarIntProp);
b.BarIntProp.bind(f.FooIntProp);
// Bindings can be unset through the property
f.FooIntProp.unbind();
// a custom converter can be passed to modify the incoming value
f.FooStringProp.bind(b.BarIntProp, custom_converter{});
}
```