[Bf-blender-cvs] [aaef6e6e3af] functions: comment on tuple
Jacques Lucke
noreply at git.blender.org
Tue Jul 2 18:39:16 CEST 2019
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. @@
More information about the Bf-blender-cvs
mailing list