Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/gilzoide/flyweight.hpp

Single header implementation of the Flyweight design pattern for C++11
https://github.com/gilzoide/flyweight.hpp

cplusplus cplusplus-11 cpp11 flyweight flyweight-container flyweight-factory flyweight-pattern single-file single-header template

Last synced: about 18 hours ago
JSON representation

Single header implementation of the Flyweight design pattern for C++11

Awesome Lists containing this project

README

        

# flyweight.hpp
Single header implementation of the [Flyweight](https://en.wikipedia.org/wiki/Flyweight_pattern) design pattern for C++11.

## Features
- Single header: copy [flyweight.hpp](flyweight.hpp) to your project, `#include "flyweight.hpp"` and that's it
- Supports C++11 and above
- Generic implementation that supports any value type, as well as any number of argument types
- Use `flyweight::get` to get values.
The first time a set of arguments is passed, the value will be created.
Subsequent calls with the same parameters return the same value reference.
- Use `flyweight::release` to release values, destroying them and releasing memory
- Supports custom creator functors when the flyweight object is got for the first time
- Supports custom deleter functors when the object is released
- Use `flyweight::get_autorelease` for a RAII idiom that automatically releases values
- Alternative `flyweight_refcounted` that employs reference counting.
Reference counts are incremented when calling `get` and decremented when calling `release`.
The value is destroyed only when the reference count reaches zero.

## Usage example
String interning implementation:
```cpp
#include
#include
#include
#include "flyweight.hpp"

// 1. Define your flyweight instance.
// By default, values are simply constructed using the arguments passed to `get`,
// but in this case we'll define custom creator/deleter functions.
flyweight::flyweight interned_strings {
// (optional) Pass a creator functor that will be called to create values.
// We duplicate the string to ensure only one version of it exists and is stable,
// even when the string_view key is copied.
[](std::string_view key) {
return std::string_view {
strndup(key.data(), key.size()),
key.size(),
};
},
// (optional) Pass a deleter functor that will be called when values are released.
// In this case, we free the allocated heap data.
[](std::string_view& value) {
free(const_cast(value.data()));
},
};

// 2. Get values.
// The first time the value will be created.
std::string_view& some_string = interned_strings.get("some string");
assert(interned_strings.is_loaded("some string"));
assert(some_string == "some string");
// Subsequent gets will return the same reference to the same value.
std::string_view& also_some_string = interned_strings.get("some string");
assert(&some_string == &also_some_string);
assert(some_string.data() == also_some_string.data());

// 3. Release values when you don't need/want them anymore.
interned_strings.release("some string");
assert(!interned_strings.is_loaded("some string"));
```

File data caching with reference counting:
```cpp
#include
#include
#include
#include "flyweight.hpp"

using file_data = std::vector;

// 1. Define your flyweight instance.
// In this case, we use the reference count enabled flyweight implementation.
flyweight::flyweight_refcounted file_data_cache {
// (optional) Pass a creator functor that will be called to create values.
[](const std::string& file_name) {
file_data data;
// read file data into vector...
return data;
},
// (optional) Pass a deleter functor that will be called when values are released.
// No need in this case, std::vector will delete the memory automatically when released.
};

// 2. Get values.
// The first time the value will be created.
file_data& file1_data = file_data_cache.get("file1");
assert(file_data_cache.is_loaded("file1"));
// At this point, the reference count for "file1" is 1
assert(file_data_cache.reference_count("file1") == 1);
// Subsequent gets will increment the reference count by 1.
file_data& also_file1_data = file_data_cache.get("file1");
assert(file_data_cache.reference_count("file1") == 2);

// 3. Release values when you don't need/want them anymore.
// This decrements the reference count by 1.
file_data_cache.release("file1");
assert(file_data_cache.reference_count("file1") == 1);
// The value is only actually unloaded when the reference count reaches zero.
assert(file_data_cache.is_loaded("file1"));

// 4. You can use `get_autorelease` for RAII style automatic release to the flyweight.
{
auto autoreleased_file1_data = file_data_cache.get_autorelease("file1");
assert(file_data_cache.reference_count("file1") == 2);
// Autoreleased values are simply wrappers to the original value reference.
file_data& file1_data_again = autoreleased_file1_data.value;
assert(&file1_data_again == &file1_data);
}
// At this point, `autoreleased_file1_data` released "file1" back to the flyweight.
assert(file_data_cache.reference_count("file1") == 1);

// 5. If you ever need it, call `clear` to release all values.
// This may be used, for example, to clear cache objects when memory is running low.
file_data_cache.clear();
```

## Integrating with CMake
You can integrate flyweight.hpp with CMake targets by adding a copy of this repository and linking with the `flyweight.hpp` target:
```cmake
add_subdirectory(path/to/flyweight.hpp)
target_link_libraries(my_awesome_target flyweight.hpp)
```