Commit: aaef6e6e3af5484ab922d1c55b1eb7ccd5080625 Author: Jacques Lucke Date: Tue Jul 2 18:38:06 2019 +0200 Branches: functions https://developer.blender.org/rBaaef6e6e3af5484ab922d1c55b1eb7ccd5080625
comment on tuple =================================================================== M source/blender/functions/backends/tuple/tuple.cpp M source/blender/functions/backends/tuple/tuple.hpp =================================================================== diff --git a/source/blender/functions/backends/tuple/tuple.cpp b/source/blender/functions/backends/tuple/tuple.cpp index 98dabd1de89..805ff2246dc 100644 --- a/source/blender/functions/backends/tuple/tuple.cpp +++ b/source/blender/functions/backends/tuple/tuple.cpp @@ -2,6 +2,24 @@ namespace FN { +TupleMeta::TupleMeta(ArrayRef<SharedType> types) : m_types(types) +{ + m_all_trivially_destructible = true; + m_size__data = 0; + for (const SharedType &type : types) { + CPPTypeInfo *info = type->extension<CPPTypeInfo>(); + m_offsets.append(m_size__data); + m_type_info.append(info); + m_size__data += info->size_of_type(); + if (!info->trivially_destructible()) { + m_all_trivially_destructible = false; + } + } + m_offsets.append(m_size__data); + + m_size__data_and_init = m_size__data + this->element_amount(); +} + void Tuple::print_initialized(std::string name) { std::cout << "Tuple: " << name << std::endl; diff --git a/source/blender/functions/backends/tuple/tuple.hpp b/source/blender/functions/backends/tuple/tuple.hpp index f66c121e54a..7818c25f9f3 100644 --- a/source/blender/functions/backends/tuple/tuple.hpp +++ b/source/blender/functions/backends/tuple/tuple.hpp @@ -1,5 +1,36 @@ #pragma once +/** + * A tuple is an array that can hold values of different types. It is the primary way to store + * values of C++ types that you don't know the exact type from. + * + * Every tuple links to a TupleMeta instance which contains meta-information about the tuple. Among + * others it knows which types are stored in the tuple and at which offsets. Furthermore, it owns + * references to the types. The assumption here is that tuples are much more often created than + * meta objects. Doing reference counting every time a tuple is created would result in lot of + * synchronization overhead. + * + * Currently, tuples only have normal pointers to their meta objects. So it can be invalidated when + * it outlives the meta object. In the future it might be necessary to allow tuples to optionally + * own the tuple meta object, so that it cannot be removed as long as it exists. + * + * Tuples can be allocated entirely on the stack to avoid heap allocations. However, due to their + * dynamic nature, the required memory can differ. There is a macro to simplify the process of + * allocating a tuple on the stack. + * + * A tuple can own the array containing the objects or not, depending on the use case. + * + * Every element in the tuple is either initialized or uninitialized. It is explicitely tracked + * what is the case. + * + * The accessors to the tuple fall into two categories: + * - Dynamic: When the caller does not know statically which types the tuple contains, it has to + * use generic methods. This is less efficient since there might be multiple virtual function + * calls. + * - Static: Sometimes, the caller knows exactly, which types are at every index in the tuple. In + * that case, this information can be used to increase performance and to get a nicer API. + */ + #include "cpp_types.hpp" namespace FN { @@ -14,54 +45,59 @@ class TupleMeta : public RefCountedBase { bool m_all_trivially_destructible; public: - TupleMeta(ArrayRef<SharedType> types = {}) : m_types(types) - { - m_all_trivially_destructible = true; - m_size__data = 0; - for (const SharedType &type : types) { - CPPTypeInfo *info = type->extension<CPPTypeInfo>(); - m_offsets.append(m_size__data); - m_type_info.append(info); - m_size__data += info->size_of_type(); - if (!info->trivially_destructible()) { - m_all_trivially_destructible = false; - } - } - m_offsets.append(m_size__data); - - m_size__data_and_init = m_size__data + this->element_amount(); - } + TupleMeta(ArrayRef<SharedType> types = {}); + /** + * Get an array containing the types of tuples using the meta object. + */ const ArrayRef<SharedType> types() const { return m_types; } + /** + * Get an array containing the CPPTypeInfo instances of all types. + */ const ArrayRef<CPPTypeInfo *> type_infos() const { return m_type_info; } + /** + * Get an array containing the byte offsets of every element in the array. + */ const ArrayRef<uint> offsets() const { return m_offsets; } + /** + * Get the required byte size to store all values in the tuple. + */ uint size_of_data() const { return m_size__data; } + /** + * Get the size of the boolean buffer that tracks which elements are initialized. + */ uint size_of_init() const { return m_size__data_and_init - m_size__data; } + /** + * Get the size of the data and initialize buffers combined. + */ uint size_of_data_and_init() const { return m_size__data_and_init; } + /** + * Get the buffer size that is required to construct the entire tuple in. + */ inline uint size_of_full_tuple() const; uint element_amount() const @@ -69,11 +105,18 @@ class TupleMeta : public RefCountedBase { return m_types.size(); } + /** + * Get the byte size of a specific element. + */ uint element_size(uint index) const { return m_offsets[index + 1] - m_offsets[index]; } + /** + * Return when all types are trivially destructible. Otherwise false. + * When all types are destructible, no deallocation loop has to run. + */ bool all_trivially_destructible() const { return m_all_trivially_destructible; @@ -114,6 +157,10 @@ class Tuple { { } + /** + * Build a new tuple in the prepared buffer. The memory in the buffer is expected to be + * uninitialized. Furthermore, the buffer must be large enough to hold the entire tuple. + */ static Tuple &ConstructInBuffer(TupleMeta &meta, void *buffer) { Tuple *tuple = new (buffer) Tuple(meta, (char *)buffer + sizeof(Tuple)); @@ -134,16 +181,20 @@ class Tuple { } } + /** + * Copy a value of type T to the given index. The caller is expected to know that T actually + * belongs to this type. + */ template<typename T> inline void copy_in(uint index, const T &value) { BLI_assert(index < m_meta->element_amount()); BLI_assert(sizeof(T) == m_meta->element_size(index)); + T *dst = (T *)this->element_ptr(index); if (std::is_trivial<T>::value) { - std::memcpy(this->element_ptr(index), &value, sizeof(T)); + std::memcpy(dst, &value, sizeof(T)); } else { - T *dst = (T *)this->element_ptr(index); if (m_initialized[index]) { std::copy_n(&value, 1, dst); @@ -156,6 +207,9 @@ class Tuple { m_initialized[index] = true; } + /** + * Copy a value from src to the given index in the tuple. + */ inline void copy_in__dynamic(uint index, void *src) { BLI_assert(index < m_meta->element_amount()); @@ -173,6 +227,11 @@ class Tuple { } } + /** + * Move a value of type T into the tuple. Note, that the destructor on the original object will + * not be called, because this will usually be done automatically when it goes out of scope. + * The caller is expected to know that the type T actually belongs to this index. + */ template<typename T> inline void move_in(uint index, T &value) { BLI_assert(index < m_meta->element_amount()); @@ -189,6 +248,9 @@ class Tuple { } } + /** + * Copy the value from src into the tuple and destroy the original value at src. + */ inline void relocate_in__dynamic(uint index, void *src) { BLI_assert(index < m_meta->element_amount()); @@ -206,12 +268,20 @@ class Tuple { } } + /** + * Copy the value to the given index. This method only works with trivial types. + */ template<typename T> inline void set(uint index, const T &value) { static_assert(std::is_trivial<T>::value, "this method can be used with trivial types only"); this->copy_in<T>(index, value); } + /** + * Return a copy of the value at the given index. The caller is expected to know that the index + * actually contains a value of type T. + * Asserts when the value was not initialized. + */ template<typename T> inline T copy_out(uint index) const { BLI_assert(index < m_meta->element_amount()); @@ -221,6 +291,12 @@ class Tuple { return *(T *)this->element_ptr(index); } + /** + * Return the value at the given index and destroy the value in the tuple. Afterwards, this index + * will contain uininitialized memory. The caller is expected to know that T is the correct type + * for that index. + * Asserts when the value was not initialized. + */ template<typename T> inline T relocate_out(uint index) const { BLI_assert(index < m_meta->element_amount()); @@ -235,6 +311,10 @@ class Tuple { return tmp; } + /** + * Copy the value from the tuple into the dst buffer. + * Asserts when the value was not initialized. + */ inline void relocate_out__dynamic(uint index, void *dst) const { BLI_assert(index < m_meta->element_amount()); @@ -249,12 +329,21 @@ class Tuple { m_initialized[index] = false; } + /** + * Return a copy of the value in the tuple at the given index. This only works with trivial + * types. + * Asserts when the value was not initialized. + */ template<typename T> inline T get(uint index) const { static_assert(std::is_trivial<T>::value, "this method can be used with trivial types only"); return this->copy_out<T>(index); } + /** + * Return a reference to a value in the tuple. + * Asserts when the value is not initialized. + */ template<typename T> inline T &get_ref(uint index) const { BLI_assert(index < m_meta->element_amount()); @@ -262,12 +351,19 @@ class Tuple { return this->element_ref<T>(index); } + /** + * Return true when the value at the given index is initialized, otherwise false. + */ inline bool is_initialized(uint index) const { BLI_assert(index < m_meta->element_amount()); return m_initialized[index]; } + /** + * Copy a value between two different location in different tuples. + * Asserts when the source value is not initialized @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org https://lists.blender.org/mailman/listinfo/bf-blender-cvs