flatten 20260225
This commit is contained in:
188
tjp/core/extensible/Extensible+CacheViaArray.h
Normal file
188
tjp/core/extensible/Extensible+CacheViaArray.h
Normal file
@@ -0,0 +1,188 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Extensible+Enumerated.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
void onExtensibleCacheNew_(void *);
|
||||
void onExtensibleCache_(void *, size_t);
|
||||
void onExtensibleCacheDelete_(void *, size_t);
|
||||
void onExtensibleCacheClear_(void *, size_t);
|
||||
|
||||
inline void onExtensibleCacheNew(void *t) { onExtensibleCacheNew_(t); }
|
||||
inline void onExtensibleCache(void *t, size_t v) { onExtensibleCache_(t, v); }
|
||||
inline void onExtensibleCacheDelete(void *t, size_t v) { onExtensibleCacheDelete_(t, v); }
|
||||
inline void onExtensibleCacheClear(void *t, size_t v) { onExtensibleCacheDelete_(t, v); }
|
||||
#else
|
||||
inline void onExtensibleCacheNew(void *) {}
|
||||
inline void onExtensibleCache(void *, size_t) {}
|
||||
inline void onExtensibleCacheDelete(void *, size_t) {}
|
||||
inline void onExtensibleCacheClear(void *, size_t) {}
|
||||
#endif
|
||||
|
||||
struct ExtensibleCacheViaArray
|
||||
{
|
||||
[[no_unique_address]]
|
||||
TestMutex mutex;
|
||||
|
||||
typedef ExtensionInterface *ExtensionInterfacePtr;
|
||||
|
||||
constexpr static size_t REDIRECT_TYPE_BITSIZE = 8;
|
||||
constexpr static size_t REDIRECTS_PER_BYTE = 8 / REDIRECT_TYPE_BITSIZE;
|
||||
|
||||
// leave one left over for the size variable
|
||||
constexpr static size_t MAX_TYPE = 48 - REDIRECTS_PER_BYTE;
|
||||
|
||||
constexpr static size_t REDIRECT_CACHE_BYTE_SIZE = MAX_TYPE / REDIRECTS_PER_BYTE;
|
||||
constexpr static size_t
|
||||
MAX_CACHE = (1 << REDIRECT_TYPE_BITSIZE) - 1
|
||||
- 1; // 0xF is NULL_REDIRECT
|
||||
|
||||
constexpr static u8 CACHE_MASK = (1 << REDIRECT_TYPE_BITSIZE) - 1;
|
||||
|
||||
constexpr static size_t NULL_REDIRECT = (1 << REDIRECT_TYPE_BITSIZE) - 1;
|
||||
|
||||
// using Cache = InPlaceArray<ExtensionInterfacePtr, MAX_CACHE>;
|
||||
using Cache = StackArray<ExtensionInterfacePtr, 2>;
|
||||
// using Cache = Vector<ExtensionInterfacePtr>;
|
||||
Cache cache;
|
||||
|
||||
using Redirects = u8[REDIRECT_CACHE_BYTE_SIZE];
|
||||
Redirects redirect;
|
||||
|
||||
ExtensibleCacheViaArray()
|
||||
{
|
||||
onExtensibleCacheNew(this);
|
||||
|
||||
memset(redirect, CACHE_MASK, REDIRECT_CACHE_BYTE_SIZE);
|
||||
}
|
||||
|
||||
~ExtensibleCacheViaArray()
|
||||
{
|
||||
debug_assert(cache.empty());
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
onExtensibleCacheDelete(this, cache.size());
|
||||
|
||||
memset(redirect, 0xFF, REDIRECT_CACHE_BYTE_SIZE);
|
||||
|
||||
for (auto p : cache)
|
||||
delete p;
|
||||
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
inline
|
||||
size_t getRedirect(size_t index)
|
||||
{
|
||||
debug_assert(index < MAX_TYPE);
|
||||
|
||||
if constexpr(REDIRECTS_PER_BYTE == 1)
|
||||
{
|
||||
return redirect[index] ;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto redirectByte = index / REDIRECTS_PER_BYTE;
|
||||
auto redirectNibble = index % REDIRECTS_PER_BYTE;
|
||||
auto redirectShift = redirectNibble * REDIRECT_TYPE_BITSIZE;
|
||||
|
||||
auto byte = redirect[redirectByte] >> redirectShift;
|
||||
return byte & CACHE_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void setRedirect(size_t index, u8 value)
|
||||
{
|
||||
debug_assert(index < MAX_TYPE);
|
||||
|
||||
if constexpr(REDIRECTS_PER_BYTE == 1)
|
||||
{
|
||||
redirect[index] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto redirectByte = index / REDIRECTS_PER_BYTE;
|
||||
auto redirectNibble = index % REDIRECTS_PER_BYTE;
|
||||
auto redirectShift = redirectNibble * REDIRECT_TYPE_BITSIZE;
|
||||
|
||||
u8 writeBits = 0xF << redirectShift;
|
||||
u8 keepBits = ~writeBits;
|
||||
auto &byte = redirect[redirectByte];
|
||||
byte = (byte & keepBits) | (value << redirectShift);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *find()
|
||||
{
|
||||
auto lock = lock_of(mutex);
|
||||
(void)lock;
|
||||
|
||||
auto index = T::getExtensionIndex();
|
||||
debug_assert(index < MAX_TYPE);
|
||||
|
||||
auto redirect = getRedirect(index);
|
||||
if (redirect == NULL_REDIRECT)
|
||||
return nullptr;
|
||||
|
||||
debug_assert(redirect < MAX_CACHE);
|
||||
|
||||
return static_cast<T *>(cache[redirect]);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void insert(T *t)
|
||||
{
|
||||
auto lock = lock_of(mutex);
|
||||
(void)lock;
|
||||
|
||||
auto index = T::getExtensionIndex();
|
||||
debug_assert(index < MAX_TYPE);
|
||||
|
||||
auto redirect = getRedirect(index);
|
||||
if (redirect != NULL_REDIRECT)
|
||||
{
|
||||
debug_assert(cache[redirect] == nullptr);
|
||||
cache[redirect] = t;
|
||||
return;
|
||||
}
|
||||
|
||||
redirect = cache.size();
|
||||
cache.push_back(t);
|
||||
|
||||
onExtensibleCache(this, cache.size());
|
||||
debug_assert(redirect < MAX_CACHE);
|
||||
|
||||
setRedirect(index, redirect);
|
||||
debug_assert(redirect < MAX_CACHE);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void erase(T *t)
|
||||
{
|
||||
auto lock = lock_of(mutex);
|
||||
(void)lock;
|
||||
|
||||
auto index = T::getExtensionIndex();
|
||||
debug_assert(index < MAX_TYPE);
|
||||
|
||||
auto redirect = getRedirect(index);
|
||||
if (redirect == NULL_REDIRECT)
|
||||
return ;
|
||||
|
||||
debug_assert(redirect < MAX_CACHE);
|
||||
cache[redirect] = nullptr;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
using ExtensibleCache = ExtensibleCacheViaArray;
|
||||
|
||||
template<typename T>
|
||||
using Extension = ExtensionEnumerated<T>;
|
||||
72
tjp/core/extensible/Extensible+CacheViaArrayAndMap.h
Normal file
72
tjp/core/extensible/Extensible+CacheViaArrayAndMap.h
Normal file
@@ -0,0 +1,72 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Extensible+CacheViaMap.h"
|
||||
#include "Extensible+Enumerated.h"
|
||||
|
||||
struct ExtensibleCacheViaArrayAndMap
|
||||
{
|
||||
typedef ExtensionInterface *ExtensionInterfacePtr;
|
||||
|
||||
constexpr static size_t MAX_CACHE=64;
|
||||
ExtensionInterfacePtr cache[MAX_CACHE];
|
||||
ExtensibleCacheViaMap map;
|
||||
|
||||
ExtensibleCacheViaArrayAndMap()
|
||||
{
|
||||
memset(cache, 0, sizeof(ExtensionInterfacePtr) * MAX_CACHE);
|
||||
}
|
||||
|
||||
~ExtensibleCacheViaArrayAndMap()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
auto *begin = &cache[0];
|
||||
auto *end = begin + MAX_CACHE;
|
||||
for (auto *p=begin; p!=end; ++p)
|
||||
{
|
||||
if (*p)
|
||||
{
|
||||
delete *p;
|
||||
*p = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
map.clear();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *find()
|
||||
{
|
||||
auto index = T::getExtensionIndex();
|
||||
if (index < MAX_CACHE)
|
||||
return static_cast<T *>(cache[index]);
|
||||
|
||||
return map.find<T>();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void insert(T *t)
|
||||
{
|
||||
auto index = T::getExtensionIndex();
|
||||
if (index < MAX_CACHE)
|
||||
{
|
||||
cache[index] = t;
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_assert(false);
|
||||
map.insert(t);
|
||||
}
|
||||
}
|
||||
} ;
|
||||
|
||||
using ExtensibleCache = ExtensibleCacheViaArrayAndMap;
|
||||
|
||||
template<typename T>
|
||||
using Extension = ExtensionEnumerated<T>;
|
||||
51
tjp/core/extensible/Extensible+CacheViaMap.h
Normal file
51
tjp/core/extensible/Extensible+CacheViaMap.h
Normal file
@@ -0,0 +1,51 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Extensible+TypeInfo.h"
|
||||
|
||||
struct ExtensibleCacheViaMap
|
||||
{
|
||||
typedef ExtensionInterface *ExtensionInterfacePtr;
|
||||
std::map<std::type_index, ExtensionInterfacePtr> extensions;
|
||||
|
||||
~ExtensibleCacheViaMap()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
for (auto &kv : extensions)
|
||||
delete kv.second;
|
||||
|
||||
extensions.clear();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *find()
|
||||
{
|
||||
const auto key = type_id<T>();
|
||||
|
||||
auto i = extensions.find(key);
|
||||
if (i == extensions.end())
|
||||
return nullptr;
|
||||
|
||||
return static_cast<T *>(i->second);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void insert(T *t)
|
||||
{
|
||||
const auto key = type_id<T>();
|
||||
|
||||
debug_assert(extensions.find(key) == extensions.end());
|
||||
extensions.emplace(key, t);
|
||||
}
|
||||
} ;
|
||||
|
||||
//using ExtensibleCache = ExtensibleCacheViaMap;
|
||||
//
|
||||
//template<typename T>
|
||||
//using Extension = ExtensionTypeInfo<T>;
|
||||
27
tjp/core/extensible/Extensible+Enumerated.h
Normal file
27
tjp/core/extensible/Extensible+Enumerated.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
template<typename T>
|
||||
struct ExtensionEnumerated : ExtensionInterface
|
||||
{
|
||||
static size_t extension_index;
|
||||
|
||||
static size_t getExtensionIndex();
|
||||
} ;
|
||||
|
||||
size_t getNextExtensionIndex();
|
||||
|
||||
template<typename T>
|
||||
size_t ExtensionEnumerated<T>::extension_index = -1;
|
||||
|
||||
template<typename T>
|
||||
size_t ExtensionEnumerated<T>::getExtensionIndex()
|
||||
{
|
||||
if (extension_index != -1)
|
||||
return extension_index;
|
||||
|
||||
extension_index = getNextExtensionIndex();
|
||||
return extension_index;
|
||||
}
|
||||
|
||||
7
tjp/core/extensible/Extensible+TypeInfo.h
Normal file
7
tjp/core/extensible/Extensible+TypeInfo.h
Normal file
@@ -0,0 +1,7 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct ExtensionTypeInfo : ExtensionInterface
|
||||
{
|
||||
} ;
|
||||
67
tjp/core/extensible/Extensible.cpp
Normal file
67
tjp/core/extensible/Extensible.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#include "Extensible.h"
|
||||
#include <atomic>
|
||||
|
||||
#include <tjp/core/log/Log.h>
|
||||
#include <tjp/core/log/LogOf.h>
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
std::atomic<size_t> nextExtensionIndex = 0;
|
||||
size_t getNextExtensionIndex()
|
||||
{
|
||||
sLogDebug("core::extensible", logVar(nextExtensionIndex));
|
||||
return nextExtensionIndex++;
|
||||
}
|
||||
|
||||
constexpr size_t MAX_CACHE_STATISTICS = 64;
|
||||
static std::array<size_t, MAX_CACHE_STATISTICS+1> numPerSizeCurrent = {0};
|
||||
static std::array<size_t, MAX_CACHE_STATISTICS+1> numPerSizeTotals = {0};
|
||||
|
||||
void onExtensibleCache_(void *t, size_t size)
|
||||
{
|
||||
if (size < MAX_CACHE_STATISTICS)
|
||||
{
|
||||
if (size > 0)
|
||||
{
|
||||
numPerSizeCurrent[size-1]--;
|
||||
numPerSizeTotals[size-1]--;
|
||||
}
|
||||
|
||||
numPerSizeCurrent[size]++;
|
||||
numPerSizeTotals[size]++;
|
||||
}
|
||||
|
||||
|
||||
sLogDebug("core::extensible", "C " << logVar(numPerSizeCurrent));
|
||||
sLogDebug("core::extensible", "T " << logVar(numPerSizeTotals));
|
||||
}
|
||||
|
||||
void onExtensibleCacheNew_(void *t)
|
||||
{
|
||||
onExtensibleCache_(t, 0);
|
||||
}
|
||||
|
||||
void onExtensibleCacheClear_(void *t, size_t size)
|
||||
{
|
||||
if (size < MAX_CACHE_STATISTICS)
|
||||
{
|
||||
numPerSizeCurrent[size]--;
|
||||
numPerSizeTotals[size]--;
|
||||
|
||||
numPerSizeCurrent[0]++;
|
||||
numPerSizeTotals[0]++;
|
||||
}
|
||||
}
|
||||
|
||||
void onExtensibleCacheDelete_(void *t, size_t size)
|
||||
{
|
||||
numPerSizeCurrent[size]--;
|
||||
|
||||
sLogDebug("core::extensible", "C " << logVar(numPerSizeCurrent));
|
||||
sLogDebug("core::extensible", "T " << logVar(numPerSizeTotals));
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
269
tjp/core/extensible/Extensible.h
Normal file
269
tjp/core/extensible/Extensible.h
Normal file
@@ -0,0 +1,269 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string.h>
|
||||
|
||||
#include <tjp/core/exception/Exception.hpp>
|
||||
#include <tjp/core/assert/debug_assert.h>
|
||||
#include <tjp/core/algorithm/remove_const_of_var.hpp>
|
||||
#include <tjp/core/types/Types.h>
|
||||
#include <tjp/core/debug/Debug.h>
|
||||
|
||||
#include <tjp/core/containers/InPlaceArray.hpp>
|
||||
#include <tjp/core/containers/StackArray.hpp>
|
||||
#include <tjp/core/threads/SpinMutex.hpp>
|
||||
#include <tjp/core/threads/Lock.hpp>
|
||||
#include <tjp/core/threads/TestMutex.hpp>
|
||||
|
||||
#include "ExtensibleWith.h"
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
struct ExtensionInterface
|
||||
{
|
||||
ExtensionInterface (const ExtensionInterface &) = delete;
|
||||
ExtensionInterface () {}
|
||||
|
||||
virtual ~ExtensionInterface () {}
|
||||
|
||||
virtual void initializeExtension() {}
|
||||
} ;
|
||||
|
||||
template<typename T>
|
||||
void initializeExtension(T *t)
|
||||
{
|
||||
t->initializeExtension();
|
||||
}
|
||||
|
||||
//#include "Extensible+CacheViaArrayAndMap.h"
|
||||
#include "Extensible+CacheViaArray.h"
|
||||
|
||||
// -----------------
|
||||
|
||||
struct ExtensibleBaseDefault
|
||||
{
|
||||
virtual ~ExtensibleBaseDefault () {};
|
||||
} ;
|
||||
|
||||
template<typename B=ExtensibleBaseDefault>
|
||||
struct Extensor : B
|
||||
{
|
||||
typedef B Super;
|
||||
mutable ExtensibleCache extensions;
|
||||
|
||||
Extensor () { }
|
||||
|
||||
template<typename ...T>
|
||||
Extensor(T &&...t) :
|
||||
B(std::forward<T>(t)...) {}
|
||||
|
||||
virtual ~Extensor ()
|
||||
{
|
||||
extensions.clear();
|
||||
}
|
||||
|
||||
template<typename U, typename T, typename std::enable_if<std::is_constructible<T, U*>::value>::type* = nullptr>
|
||||
T &getExtensionTU (U *self) const
|
||||
{
|
||||
if (auto extension = extensions.find<T>())
|
||||
return *extension;
|
||||
|
||||
T *t = new T(
|
||||
static_cast<U *>(self)
|
||||
);
|
||||
|
||||
extensions.insert(t);
|
||||
initializeExtension(t);
|
||||
|
||||
return *t;
|
||||
}
|
||||
|
||||
template<typename U, typename T, typename std::enable_if<!std::is_constructible<T, U*>::value>::type* = nullptr>
|
||||
T &getExtensionTU (U *self) const
|
||||
{
|
||||
if (auto extension = extensions.find<T>())
|
||||
return *extension;
|
||||
|
||||
throw tjp::core::Exception("Extension not available");
|
||||
}
|
||||
|
||||
template<typename U, typename T, typename std::enable_if<std::is_constructible<T, U*>::value>::type* = nullptr>
|
||||
T &getExtensionU () const
|
||||
{
|
||||
if (auto extension = extensions.find<T>())
|
||||
return *extension;
|
||||
|
||||
T *t = new T(
|
||||
static_cast<U *>(remove_const_of_var(this))
|
||||
);
|
||||
|
||||
extensions.insert(t);
|
||||
initializeExtension(t);
|
||||
|
||||
return *t;
|
||||
}
|
||||
|
||||
template<typename U, typename T, typename std::enable_if<!std::is_constructible<T, U*>::value>::type* = nullptr>
|
||||
T &getExtensionU () const
|
||||
{
|
||||
if (auto extension = extensions.find<T>())
|
||||
return *extension;
|
||||
|
||||
throw tjp::core::Exception("Extension not available");
|
||||
}
|
||||
|
||||
template<typename U, typename T, typename std::enable_if<std::is_constructible<T, U*>::value>::type* = nullptr>
|
||||
bool hasExtensionU () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename U, typename T, typename std::enable_if<!std::is_constructible<T, U*>::value>::type* = nullptr>
|
||||
bool hasExtensionU () const
|
||||
{
|
||||
return extensions.find<T>();
|
||||
}
|
||||
|
||||
template<typename U, typename T, typename TI, typename ...V>
|
||||
T &makeExtensionTU (const U *self, V &&...v) const
|
||||
{
|
||||
if (auto extension = extensions.find<T>())
|
||||
return *extension;
|
||||
|
||||
T *t = new TI(
|
||||
static_cast<U *>(remove_const_of_var(self)),
|
||||
v...
|
||||
);
|
||||
|
||||
extensions.insert(t);
|
||||
initializeExtension(t);
|
||||
|
||||
return *t;
|
||||
}
|
||||
|
||||
template<typename U, typename T, typename TI, typename ...V>
|
||||
T &makeExtensionU (V &&...v) const
|
||||
{
|
||||
if (auto extension = extensions.find<T>())
|
||||
return *extension;
|
||||
|
||||
T *t = new TI(
|
||||
static_cast<U *>(remove_const_of_var(this)),
|
||||
v...
|
||||
);
|
||||
|
||||
extensions.insert(t);
|
||||
initializeExtension(t);
|
||||
|
||||
return *t;
|
||||
}
|
||||
|
||||
template<typename U, typename T>
|
||||
T withExtensionU () const
|
||||
{
|
||||
return T(
|
||||
ExtendingWith {},
|
||||
static_cast<U *>(remove_const_of_var(this))
|
||||
);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void eraseExtension ()
|
||||
{
|
||||
if (auto extension = extensions.find<T>())
|
||||
{
|
||||
extensions.erase(extension);
|
||||
delete extension;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename P, typename B=ExtensibleBaseDefault>
|
||||
struct Extensible : Extensor<B>
|
||||
{
|
||||
typedef Extensor<B> Extensor_;
|
||||
typedef Extensible<P,B> Extensible_;
|
||||
|
||||
Extensible () {}
|
||||
|
||||
template<typename ...T>
|
||||
Extensible(T &&...t) :
|
||||
Extensor_(std::forward<T>(t)...) {}
|
||||
|
||||
template<typename T>
|
||||
bool hasExtension () const
|
||||
{
|
||||
return this->template hasExtensionU<P, T>();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T &getExtension () const
|
||||
{
|
||||
return this->template getExtensionU<P, T>();
|
||||
}
|
||||
|
||||
template<typename T, typename TI, typename ...V>
|
||||
T &makeExtension (V &&...v) const
|
||||
{
|
||||
return this->template makeExtensionU<P, T, TI>(v...);
|
||||
}
|
||||
|
||||
template<typename T, typename ...V>
|
||||
T &makeExtension (V &&...v) const
|
||||
{
|
||||
return this->template makeExtensionU<P, T, T>(v...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T withExtension () const
|
||||
{
|
||||
return this->template withExtensionU<P, T>();
|
||||
}
|
||||
} ;
|
||||
|
||||
template<typename B, typename U>
|
||||
struct Extensibled : B
|
||||
{
|
||||
typedef B Super;
|
||||
typedef Extensibled<B, U> Extensible_;
|
||||
|
||||
template<typename ...T>
|
||||
Extensibled(T &&...t) :
|
||||
B(std::forward<T>(t)...) {}
|
||||
|
||||
Extensibled() {}
|
||||
|
||||
template<typename T>
|
||||
T &getExtension () const
|
||||
{
|
||||
return this->template getExtensionU<U, T>();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool hasExtension () const
|
||||
{
|
||||
return this->template hasExtensionU<U, T>();
|
||||
}
|
||||
|
||||
template<typename T, typename TI, typename ... V>
|
||||
T &makeExtension (V &&...v) const
|
||||
{
|
||||
return this->template makeExtensionU<U, T, TI>(v...);
|
||||
}
|
||||
|
||||
template<typename T, typename ... V>
|
||||
T &makeExtension (V &&...v) const
|
||||
{
|
||||
return this->template makeExtensionU<U, T, T>(v...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T withExtension () const
|
||||
{
|
||||
return this->template withExtensionU<U, T>();
|
||||
}
|
||||
} ;
|
||||
|
||||
} // namespace
|
||||
33
tjp/core/extensible/ExtensibleWith.h
Normal file
33
tjp/core/extensible/ExtensibleWith.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/algorithm/remove_const_of_var.hpp>
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T>
|
||||
struct WithExtension {};
|
||||
|
||||
struct ExtendingWith {};
|
||||
|
||||
template<typename P>
|
||||
struct ExtensibleWith
|
||||
{
|
||||
template<typename U, typename T>
|
||||
T withExtensionU () const
|
||||
{
|
||||
return T(
|
||||
ExtendingWith {},
|
||||
static_cast<U *>(remove_const_of_var(this))
|
||||
);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T withExtension () const
|
||||
{
|
||||
return withExtensionU<P, T>();
|
||||
}
|
||||
} ;
|
||||
|
||||
} // namespace
|
||||
130
tjp/core/extensible/_tests/Extensible.cpp
Normal file
130
tjp/core/extensible/_tests/Extensible.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#include <tjp/core/extensible/Extensible.h>
|
||||
|
||||
#include <tjp/core/testing/catch.hpp>
|
||||
|
||||
#include <tjp/core/log/Log.h>
|
||||
#include <tjp/core/log/LogOf.h>
|
||||
|
||||
namespace tjp::core {
|
||||
namespace {
|
||||
|
||||
struct A : Extensible<A>
|
||||
{
|
||||
int num = 0;
|
||||
} ;
|
||||
|
||||
struct B : Extensibled<A, B>
|
||||
{
|
||||
|
||||
} ;
|
||||
|
||||
struct C : Extensibled<B, C>
|
||||
{
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
struct A_Ext : Extension<A_Ext>
|
||||
{
|
||||
A_Ext (A *a) { a->num++; }
|
||||
|
||||
char val() const { return 'A'; }
|
||||
} ;
|
||||
|
||||
struct B_Ext : Extension<B_Ext>
|
||||
{
|
||||
B_Ext (B *b) { b->num++; }
|
||||
|
||||
char val() const { return 'B'; }
|
||||
} ;
|
||||
|
||||
struct C_Ext : Extension<C_Ext>
|
||||
{
|
||||
C_Ext (C *c) { c->num++; }
|
||||
|
||||
char val() const { return 'C'; }
|
||||
} ;
|
||||
|
||||
SCENARIO("extensible")
|
||||
{
|
||||
xLogActivateStory("core::extensible");
|
||||
|
||||
GIVEN("sizeof")
|
||||
{
|
||||
sLogTest("testing", logVar(sizeof(Extensible<A>)) << logVar(sizeof(ExtensibleCache)));
|
||||
sLogTest("testing", logVar(sizeof(ExtensibleCache::Cache)) << logVar(sizeof(ExtensibleCache::Redirects)));
|
||||
}
|
||||
|
||||
GIVEN("a get extension")
|
||||
{
|
||||
A a;
|
||||
B b;
|
||||
C c;
|
||||
|
||||
auto val = a.getExtension<A_Ext>().val() ;
|
||||
REQUIRE(val == 'A');
|
||||
REQUIRE(A_Ext::extension_index == 0);
|
||||
|
||||
REQUIRE(b.getExtension<B_Ext>().val() == 'B');
|
||||
REQUIRE(B_Ext::extension_index == 1);
|
||||
|
||||
// REQUIRE(c.getExtension<C_Ext>().val() == 'C');
|
||||
|
||||
val = c.getExtension<C_Ext>().val();
|
||||
REQUIRE(val == 'C');
|
||||
REQUIRE(C_Ext::extension_index == 2);
|
||||
|
||||
REQUIRE(a.num == 1);
|
||||
REQUIRE(b.num == 1);
|
||||
REQUIRE(c.num == 1);
|
||||
|
||||
|
||||
val = c.getExtension<A_Ext>().val() ;
|
||||
REQUIRE(val == 'A');
|
||||
|
||||
val = c.getExtension<B_Ext>().val() ;
|
||||
REQUIRE(val == 'B');
|
||||
|
||||
val = c.getExtension<C_Ext>().val() ;
|
||||
REQUIRE(val == 'C');
|
||||
|
||||
val = c.getExtension<A_Ext>().val() ;
|
||||
REQUIRE(val == 'A');
|
||||
|
||||
val = c.getExtension<B_Ext>().val() ;
|
||||
REQUIRE(val == 'B');
|
||||
|
||||
val = c.getExtension<C_Ext>().val() ;
|
||||
REQUIRE(val == 'C');
|
||||
|
||||
REQUIRE(c.num == 3);
|
||||
|
||||
}
|
||||
|
||||
GIVEN("a make extension")
|
||||
{
|
||||
A a;
|
||||
B b;
|
||||
C c;
|
||||
|
||||
auto val = a.makeExtension<A_Ext>().val() ;
|
||||
REQUIRE(val == 'A');
|
||||
|
||||
REQUIRE(b.makeExtension<B_Ext>().val() == 'B');
|
||||
REQUIRE(c.makeExtension<C_Ext>().val() == 'C');
|
||||
|
||||
val = a.getExtension<A_Ext>().val() ;
|
||||
REQUIRE(val == 'A');
|
||||
|
||||
|
||||
REQUIRE(a.num == 1);
|
||||
REQUIRE(b.num == 1);
|
||||
REQUIRE(c.num == 1);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user