diff --git a/contrib/autoboost/libs/system/src/stdafx.h b/contrib/autoboost/libs/system/src/stdafx.h new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/json11/stdafx.h b/contrib/json11/stdafx.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/autowiring/AnySharedPointer.h b/src/autowiring/AnySharedPointer.h index 792efb40f..9e2a38951 100644 --- a/src/autowiring/AnySharedPointer.h +++ b/src/autowiring/AnySharedPointer.h @@ -139,6 +139,15 @@ class AnySharedPointerT: AnySharedPointer(std::shared_ptr{}) {} + AnySharedPointerT(T&& value) : + AnySharedPointer(std::make_shared(std::forward(value))) + {} + + template + AnySharedPointerT(U&& value) : + AnySharedPointer(std::make_shared(std::forward(value))) + {} + T& operator*(void) { return *as(); } const T& operator*(void) const { return *as(); } diff --git a/src/autowiring/Autowired.h b/src/autowiring/Autowired.h index 3b9abba42..41a30ac0d 100644 --- a/src/autowiring/Autowired.h +++ b/src/autowiring/Autowired.h @@ -174,7 +174,7 @@ class Autowired: }; template - signal_relay operator()(autowiring::signal U::*sig) { + signal_relay operator()(autowiring::signal U::*sig) { static_assert(std::is_base_of::value, "Cannot reference member of unrelated type"); return signal_relay(*this, sig); } diff --git a/src/autowiring/CMakeLists.txt b/src/autowiring/CMakeLists.txt index 66742c87f..e349234b2 100644 --- a/src/autowiring/CMakeLists.txt +++ b/src/autowiring/CMakeLists.txt @@ -60,6 +60,13 @@ set(Autowiring_SRCS callable.h CallExtractor.h CallExtractor.cpp + config.h + config_descriptor.h + config_descriptor.cpp + ConfigManager.h + ConfigManager.cpp + ConfigRegistry.h + ConfigRegistry.cpp ContextEnumerator.cpp ContextEnumerator.h ContextMap.h @@ -102,6 +109,7 @@ set(Autowiring_SRCS hash_tuple.h has_autofilter.h has_autoinit.h + has_getconfigdescriptor.h has_simple_constructor.h has_static_new.h has_validate.h @@ -114,6 +122,7 @@ set(Autowiring_SRCS is_shared_ptr.h ManualThreadPool.h ManualThreadPool.cpp + marshaller.h member_new_type.h MemoEntry.h MemoEntry.cpp @@ -125,8 +134,10 @@ set(Autowiring_SRCS ObjectPool.h ObjectPoolMonitor.cpp ObjectPoolMonitor.h + observable.h once.h once.cpp + optional.h Parallel.h Parallel.cpp registration.h diff --git a/src/autowiring/ConfigManager.cpp b/src/autowiring/ConfigManager.cpp new file mode 100644 index 000000000..1b7850004 --- /dev/null +++ b/src/autowiring/ConfigManager.cpp @@ -0,0 +1,51 @@ +// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. +#include "stdafx.h" +#include "ConfigManager.h" +#include + +using namespace autowiring; + +ConfigManager::ConfigManager(void) : + m_empty("") +{} + +void ConfigManager::Register(void* pObj, const config_descriptor& desc) { + std::lock_guard lk(m_lock); + for (const auto& field : desc.fields) { + Entry& entry = m_config[field.second.name]; + entry.attached.push_back({ + &field.second, + static_cast(pObj) + field.second.offset + }); + + if (entry.value) + entry.attached.back().field->marshaller->unmarshal( + entry.attached.back().pObj, + entry.value->c_str() + ); + } +} + +const std::string& ConfigManager::Get(std::string name) const { + auto q = m_config.find(name); + if (q == m_config.end()) + return m_empty; + + return *q->second.value; +} + +void ConfigManager::Set(std::string&& name, std::string&& value) { + std::lock_guard lk(m_lock); + auto q = m_config.find(name); + if (q == m_config.end()) + q = m_config.insert(q, { std::move(name), {} }); + + Entry& entry = q->second; + entry.value = std::move(value); + + for (Entry::Attachment& attachment : entry.attached) + attachment.field->marshaller->unmarshal( + attachment.pObj, + entry.value->c_str() + ); +} diff --git a/src/autowiring/ConfigManager.h b/src/autowiring/ConfigManager.h new file mode 100644 index 000000000..a8b4550a2 --- /dev/null +++ b/src/autowiring/ConfigManager.h @@ -0,0 +1,57 @@ +// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. +#pragma once +#include "config_descriptor.h" +#include "optional.h" +#include "spin_lock.h" +#include +#include +#include + +namespace autowiring { + class ConfigManager { + public: + ConfigManager(void); + + private: + spin_lock m_lock; + + // A single entry, which has a string representation part paired + // with a pointer to the value part + struct Entry { + // The value held here + optional value; + + struct Attachment { + const config_field* field; + void* pObj; + }; + + // Fields attached on this entry: + std::vector attached; + }; + + // Empty string sentry, used when a miss occurs + const std::string m_empty; + + // All configuration values + std::unordered_map m_config; + + public: + /// + /// Registers a new configurable object + /// + /// The object that may be configured + /// A descriptor for the object + void Register(void* pObj, const config_descriptor& desc); + + /// + /// Gets the current configuration value from the map + /// + const std::string& Get(std::string name) const; + + /// + /// Sets the named config value in the map + /// + void Set(std::string&& name, std::string&& value); + }; +} diff --git a/src/autowiring/ConfigRegistry.cpp b/src/autowiring/ConfigRegistry.cpp new file mode 100644 index 000000000..ea5b4913e --- /dev/null +++ b/src/autowiring/ConfigRegistry.cpp @@ -0,0 +1,11 @@ +// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. +#include "stdafx.h" +#include "ConfigRegistry.h" + +using namespace autowiring; + +std::atomic autowiring::g_pFirstEntry; + +config_registry_entry_base::config_registry_entry_base(void) { + while (!g_pFirstEntry.compare_exchange_weak(pFlink, this)); +} diff --git a/src/autowiring/ConfigRegistry.h b/src/autowiring/ConfigRegistry.h new file mode 100644 index 000000000..b5a390ee6 --- /dev/null +++ b/src/autowiring/ConfigRegistry.h @@ -0,0 +1,64 @@ +// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. +#pragma once +#include "config_descriptor.h" +#include + +namespace autowiring { + struct config_registry_entry_base { + protected: + config_registry_entry_base(void); + + public: + // Next entry in the registry: + config_registry_entry_base* pFlink = nullptr; + }; + + template + struct config_registry_entry { + static const config_descriptor* desc(void) { return nullptr; } + }; + + template + struct config_registry_entry::value>::type> : + config_registry_entry_base + { + public: + static const config_descriptor* desc(void) { + static const config_descriptor desc = T::GetConfigDescriptor(); + return &desc; + } + }; + + extern std::atomic g_pFirstEntry; + + /// + /// Gets a string representation of the named configuration value + /// + template + std::string ConfigGet(const char* name, T& obj) { + const config_descriptor& desc = *config_registry_entry::desc(); + auto q = desc.fields.find(name); + if (q == desc.fields.end()) + throw std::invalid_argument("Configuration name not found in the specified object's configuration descriptor"); + + const autowiring::config_field& f = q->second; + return f.marshaller->marshal(reinterpret_cast(&obj) + f.offset); + } + + /// + /// Sets the named configuration value to the specified string value + /// + template + void ConfigSet(const char* name, T& obj, const char* value) { + const config_descriptor& desc = *config_registry_entry::desc(); + auto q = desc.fields.find(name); + if (q == desc.fields.end()) + throw std::invalid_argument("Configuration name not found in the specified object's configuration descriptor"); + + const autowiring::config_field& f = q->second; + f.marshaller->unmarshal( + reinterpret_cast(&obj) + f.offset, + value + ); + } +} diff --git a/src/autowiring/CoreContext.h b/src/autowiring/CoreContext.h index 84f0b46a8..150c5bedd 100644 --- a/src/autowiring/CoreContext.h +++ b/src/autowiring/CoreContext.h @@ -7,6 +7,7 @@ #include "Bolt.h" #include "CallExtractor.h" #include "CoreRunnable.h" +#include "ConfigManager.h" #include "ContextMember.h" #include "CoreContextStateBlock.h" #include "CoreObjectDescriptor.h" @@ -133,6 +134,9 @@ class CoreContext: // Asserted any time a new object is added to the context autowiring::signal newObject; + // The one and only configuration manager type + autowiring::ConfigManager Config; + virtual ~CoreContext(void); /// @@ -235,7 +239,7 @@ class CoreContext: typedef std::shared_ptr (*t_pfnCreate)( std::shared_ptr pParent, t_childList::iterator backReference - ); + ); /// \internal /// @@ -606,13 +610,20 @@ class CoreContext: std::shared_ptr retVal( CreationRules::New(*this, std::forward(args)...) ); + CoreObjectDescriptor objDesc(retVal, (T*)nullptr); + + // Configure if the object is configurable, use a static check rather than checking the + // value of pConfigDesc because it's a little faster and one necessarily follows the other + // We also want this to happen before the AutoInit call is made + if(autowiring::has_getconfigdescriptor::value) + Config.Register(static_cast(retVal.get()), *objDesc.pConfigDesc); // AutoInit if sensible to do so: CallAutoInit(*retVal, has_autoinit()); try { // Pass control to the insertion routine, which will handle injection from this point: - AddInternal(CoreObjectDescriptor(retVal, (T*)nullptr)); + AddInternal(objDesc); } catch(autowiring_error&) { // We know why this exception occurred. It's because, while we were constructing our diff --git a/src/autowiring/CoreObjectDescriptor.h b/src/autowiring/CoreObjectDescriptor.h index 946f2708a..1d59adbdf 100644 --- a/src/autowiring/CoreObjectDescriptor.h +++ b/src/autowiring/CoreObjectDescriptor.h @@ -4,6 +4,7 @@ #include "auto_id.h" #include "AutoFilterDescriptor.h" #include "BoltBase.h" +#include "ConfigRegistry.h" #include "ContextMember.h" #include "CoreObject.h" #include "CoreRunnable.h" @@ -49,6 +50,7 @@ struct CoreObjectDescriptor { pBasicThread(autowiring::fast_pointer_cast(value)), pFilter(autowiring::fast_pointer_cast(value)), pBoltBase(autowiring::fast_pointer_cast(value)), + pConfigDesc(autowiring::config_registry_entry::desc()), primitiveOffset( reinterpret_cast( static_cast( @@ -123,6 +125,9 @@ struct CoreObjectDescriptor { std::shared_ptr pFilter; std::shared_ptr pBoltBase; + // Configuration descriptor, if the object provides one + const autowiring::config_descriptor* pConfigDesc; + // Distance from TActual to T size_t primitiveOffset; }; diff --git a/src/autowiring/Decompose.h b/src/autowiring/Decompose.h index 1edec3c7e..242a40dd7 100644 --- a/src/autowiring/Decompose.h +++ b/src/autowiring/Decompose.h @@ -1,15 +1,12 @@ // Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. #pragma once #include "is_any.h" -#include #include template struct TemplatePack { static const int N = sizeof...(Ts); - typedef std::tuple t_args; - /// /// An array of type T, parameterized by the bound function's arguments /// diff --git a/src/autowiring/DecorationDisposition.h b/src/autowiring/DecorationDisposition.h index ac554abc6..e5ac2d833 100644 --- a/src/autowiring/DecorationDisposition.h +++ b/src/autowiring/DecorationDisposition.h @@ -6,25 +6,24 @@ #include #include #include -#include TYPE_INDEX_HEADER struct SatCounter; struct DecorationKey { DecorationKey(void) = default; - + explicit DecorationKey(auto_id id, int tshift) : id(id), tshift(tshift) {} - + // The type index auto_id id; // Zero refers to a decoration created on this packet, a positive number [tshift] indicates // a decoration attached [tshift] packets ago. int tshift = -1; - + bool operator==(const DecorationKey& rhs) const { return id == rhs.id && tshift == rhs.tshift; } @@ -126,7 +125,7 @@ struct DecorationDisposition // The current state of this disposition DispositionState m_state = DispositionState::Unsatisfied; - + /// /// True if all publishers have run on this disposition /// diff --git a/src/autowiring/DispatchQueue.h b/src/autowiring/DispatchQueue.h index 22d0b0a5d..4e4ca07a7 100644 --- a/src/autowiring/DispatchQueue.h +++ b/src/autowiring/DispatchQueue.h @@ -4,7 +4,6 @@ #include "DispatchThunk.h" #include "once.h" #include -#include #include #include MUTEX_HEADER #include RVALUE_HEADER diff --git a/src/autowiring/Parallel.h b/src/autowiring/Parallel.h index e426d5243..65fa81d51 100644 --- a/src/autowiring/Parallel.h +++ b/src/autowiring/Parallel.h @@ -5,7 +5,6 @@ #include "DispatchQueue.h" #include #include -#include #include class CoreContext; diff --git a/src/autowiring/auto_id.h b/src/autowiring/auto_id.h index 852a53773..4ccabd496 100644 --- a/src/autowiring/auto_id.h +++ b/src/autowiring/auto_id.h @@ -1,8 +1,6 @@ // Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. #pragma once #include "fast_pointer_cast.h" -#include -#include TYPE_INDEX_HEADER class CoreObject; diff --git a/src/autowiring/config.h b/src/autowiring/config.h new file mode 100644 index 000000000..a39626d0f --- /dev/null +++ b/src/autowiring/config.h @@ -0,0 +1,119 @@ +// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. +#pragma once +#include "marshaller.h" +#include "spin_lock.h" +#include + +namespace autowiring { + /// + /// A configuration value tracker class used with the configuration systems + /// + /// + /// This class uses a double-buffered value approach to avoid synchronization requirements + /// under asynchronous update. It assumes a single point of use and multiple points of + /// update. The clear_dirty call can be used to simaltaneously switch the active value + /// buffer and also clear the dirty flag. + /// + template + struct config { + config(void) = default; + config(const config&) = delete; + config(config&& rhs): + dirty(rhs.dirty), + valueIndex(rhs.valueIndex) + { + values[0] = std::move(rhs.values[0]); + values[1] = std::move(rhs.values[1]); + } + + explicit config(const T& value) { values[0] = value; } + explicit config(T&& value) { values[0] = std::move(value); } + + template + explicit config(U&& value) { + values[0] = std::forward(value); + } + + private: + // Lock, used to move the asyncrhonous value to the value field + autowiring::spin_lock lock; + + // Tracks whether the backing value has been updated + bool dirty = true; + + // The index of the currently active value + size_t valueIndex = 0; + + // The active value, advertised by accessors, and the dirty value, accessed + // by assigners. + std::array values; + + public: + bool is_dirty(void) const { return dirty; } + const T& get(void) const { return values[valueIndex]; } + + /// + /// Clears the dirty bit and returns its value prior to the clear + /// + /// True if the field was dirty, false otherwise + /// + /// This method must be synchronized with all accessors. + /// + bool clear_dirty(void) { + if (!dirty) + return false; + + std::lock_guard lk(lock); + valueIndex = !valueIndex; + dirty = false; + return true; + } + + operator const T&(void) const { return get(); } + const T* operator->(void) const { return &get(); } + const T& operator*(void) const { return get(); } + bool operator==(const T& rhs) const { return get() == rhs; } + bool operator!=(const T& rhs) const { return get() != rhs; } + + config& operator=(const config& value) { + // Local copy to hold the spin lock a minimum amount of time + return *this = value.get(); + } + + template + config& operator=(const U& rhs) { + return *this = T{ rhs }; + } + + template + config& operator=(U&& rhs) { + std::lock_guard lk(lock); + this->values[!valueIndex] = std::forward(rhs); + dirty = true; + return *this; + } + }; + + template + bool operator==(const T& lhs, const config& rhs) { return rhs == lhs; } + + template + struct marshaller> : + marshaller_base + { + typedef autowiring::config type; + + // Marshaller for the interior type + marshaller interior; + + std::string marshal(const void* ptr) const override { + return interior.marshal(&static_cast(ptr)->get()); + } + + void unmarshal(void* ptr, const char* szValue) const override { + T value; + interior.unmarshal(&value, szValue); + *static_cast(ptr) = std::move(value); + } + }; +} diff --git a/src/autowiring/config_descriptor.cpp b/src/autowiring/config_descriptor.cpp new file mode 100644 index 000000000..b3f1ab1cd --- /dev/null +++ b/src/autowiring/config_descriptor.cpp @@ -0,0 +1,10 @@ +// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. +#include "stdafx.h" +#include "config_descriptor.h" + +autowiring::config_descriptor::t_mpType autowiring::config_descriptor::MakeFields(const std::initializer_list& fields) { + t_mpType retVal; + for (auto& field : fields) + retVal[field.name] = field; + return retVal; +} diff --git a/src/autowiring/config_descriptor.h b/src/autowiring/config_descriptor.h new file mode 100644 index 000000000..fb098d848 --- /dev/null +++ b/src/autowiring/config_descriptor.h @@ -0,0 +1,90 @@ +// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. +#pragma once +#include "AnySharedPointer.h" +#include "has_getconfigdescriptor.h" +#include "marshaller.h" +#include +#include +#include + +namespace autowiring { + template + struct marshaller; + + template + static const marshaller& get_marshaller(void) { + // To fix this, specialize autowiring::marshal for your type + static_assert( + !std::is_base_of>::value, + "Autowiring does not provide a built-in marshaller for type T" + ); + + // To fix this, ensure your specialization implements marshal_base + static_assert( + std::is_base_of> ::value, + "Marshaller for type T does not implement the marshal_base interface" + ); + + static const marshaller sc_marshaller{}; + return sc_marshaller; + } + + struct config_field { + config_field(void) = default; + + template + config_field(const char* name, const char* desc, U T::* memptr, V&& default_value) : + name(name), + description(desc), + offset(reinterpret_cast(&(static_cast(nullptr)->*memptr))), + default_value(std::make_shared::type>()), + marshaller{ &get_marshaller() } + {} + + template + config_field(const char* name, U T::* memptr) : + name(name), + offset(reinterpret_cast(&(static_cast(nullptr)->*memptr))), + marshaller{ &get_marshaller() }, + default_value(std::make_shared::type>()) + {} + + // Name and optional description + const char* name = nullptr; + const char* description = nullptr; + + // Offset from the base of the object to the member to be serialized + size_t offset = 0; + + // Default value for this type, if one exists: + AnySharedPointer default_value; + + // Pointer to the required marshaller singleton: + const marshaller_base* marshaller = nullptr; + }; + + struct string_hash { + size_t operator()(const char* p) const { + size_t rv = 5381; + for (; *p; p++) + rv += (rv << 5) + *p; + return rv; + } + + bool operator()(const char* lhs, const char* rhs) const { + return !strcmp(lhs, rhs); + } + }; + + struct config_descriptor { + typedef std::unordered_map t_mpType; + + static t_mpType MakeFields(const std::initializer_list& fields); + + config_descriptor(std::initializer_list fields) : + fields(MakeFields(fields)) + {} + + const t_mpType fields; + }; +} diff --git a/src/autowiring/demangle.cpp b/src/autowiring/demangle.cpp index c86a8536b..34d9d5f40 100644 --- a/src/autowiring/demangle.cpp +++ b/src/autowiring/demangle.cpp @@ -2,6 +2,8 @@ #include "stdafx.h" #include "demangle.h" #include "AnySharedPointer.h" +#include +#include #if __GNUG__ // Mac and linux #include @@ -16,7 +18,7 @@ static std::string demangle_name(const char* name) { if(status != 0) return std::string(); - + return std::string(res.get()); } @@ -36,7 +38,7 @@ static std::string demangle_name(const char* name) { name += 6; continue; } - + if (strncmp(name, "struct ", 7) == 0) { name += 7; continue; diff --git a/src/autowiring/demangle.h b/src/autowiring/demangle.h index 3f4997dcf..5600a4c27 100644 --- a/src/autowiring/demangle.h +++ b/src/autowiring/demangle.h @@ -1,6 +1,7 @@ // Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. #pragma once #include +#include #include struct AnySharedPointer; diff --git a/src/autowiring/has_getconfigdescriptor.h b/src/autowiring/has_getconfigdescriptor.h new file mode 100644 index 000000000..bc1655fbb --- /dev/null +++ b/src/autowiring/has_getconfigdescriptor.h @@ -0,0 +1,41 @@ +// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. +#pragma once +#include TYPE_TRAITS_HEADER +#include RVALUE_HEADER + +namespace autowiring { + struct config_descriptor; + + /// + /// Utility helper structure for types which have a factory New routine + /// + /// + /// A factory New routine is malformed when the return type is not implicitly castable to type T + /// + template + struct has_well_formed_getconfigdescriptor { + static const bool value = + std::is_convertible< + typename std::result_of::type, + autowiring::config_descriptor + >::value && + !std::is_member_function_pointer::value; + }; + + template + struct has_well_formed_getconfigdescriptor { + static const bool value = false; + }; + + template + struct has_getconfigdescriptor + { + template + static std::true_type select(decltype(U::GetConfigDescriptor)*); + + template + static std::false_type select(...); + + static const bool value = has_well_formed_getconfigdescriptor(nullptr))>::value; + }; +} diff --git a/src/autowiring/marshaller.h b/src/autowiring/marshaller.h new file mode 100644 index 000000000..bedd3224c --- /dev/null +++ b/src/autowiring/marshaller.h @@ -0,0 +1,263 @@ +// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include TYPE_TRAITS_HEADER + +namespace autowiring { + struct invalid_marshal_base {}; + + template + struct marshaller; + + /// + /// The interface all marshallers must support + /// + struct marshaller_base { + /// + /// Marshals the underlying type to a string value + /// + virtual std::string marshal(const void* ptr) const { return ""; } + + /// + /// Converts the specified string value to the output type + /// + /// The string value to be converted + /// A pointer to the memory region where the output will be stored + virtual void unmarshal(void* ptr, const char* szValue) const = 0; + }; + + /// + /// Built-in marshaller, declared separately because it's a little more complicated + /// + template + struct builtin_marshaller : + invalid_marshal_base + {}; + + template<> + struct builtin_marshaller : + marshaller_base + { + typedef bool type; + + std::string marshal(const void* ptr) const override { + return *static_cast(ptr) ? "true" : "false"; + } + + void unmarshal(void* ptr, const char* szValue) const override { + if (!strcmp("true", szValue)) + *static_cast(ptr) = true; + else if (!strcmp("false", szValue)) + *static_cast(ptr) = false; + else + throw std::invalid_argument("Boolean unmarshaller expects true or false keyword"); + } + }; + + template<> + struct builtin_marshaller : + builtin_marshaller + {}; + + template + struct builtin_marshaller::value>::type> : + marshaller_base + { + typedef typename std::remove_volatile::type type; + + std::string marshal(const void* ptr) const override { + std::string retVal; + type val = *static_cast(ptr); + bool pos = 0 < val; + for (; val; val /= 10) { + retVal.push_back(static_cast(val % 10 + '0')); + } + if (!pos) + retVal.push_back('-'); + + for ( + auto first = retVal.begin(), last = retVal.end(); + (first != last) && (first != --last); + ++first + ) + std::swap(*first, *last); + return retVal; + } + + void unmarshal(void* ptr, const char* szValue) const override { + type& value = *static_cast(ptr); + bool negative = *szValue == '-'; + if(negative) { + // Skip over the sign, verify we aren't assigning to the wrong field type + szValue++; + if (std::is_unsigned::value) + throw std::range_error("Attempted to set a signed value on an unsigned calibration field"); + } + + for (value = 0; *szValue; szValue++) { + if (*szValue < '0' || '9' < *szValue) + throw std::invalid_argument("String value is not an integer"); + value = *szValue - '0' + value * 10; + } + if(negative) + value *= -1; + } + }; + + template + struct builtin_marshaller::value>::type> : + marshaller_base + { + typedef typename std::remove_volatile::type type; + + std::string marshal(const void* ptr) const override { + std::string retVal; + type value = *static_cast(ptr); + if (value == 0.0f) + return "0"; + + bool neg = value < 0.0f; + if (neg) + value *= -1.0f; + + // Convert input value to scientific notation + int power = static_cast(std::log10(value)); + + // We will be assembling the number backwards, need to keep track of the + // current digit + int curDigit = std::numeric_limits::digits10 - power; + + // We only want a certain number of digits, this digit count will fit in + // a large integer and elimintes the loss of precision we experience when + // using floating point math to try to do digit shifts + uint64_t digits = static_cast(value * std::pow(10.0, curDigit)); + + // Trailing zero introduction + if (power > std::numeric_limits::digits10) + retVal.append(power - std::numeric_limits::digits10, '0'); + + // Trailing zero omission + while (0 < curDigit && 0 == (digits % 10)) { + digits /= 10; + curDigit--; + } + + for(; digits; digits /= 10, curDigit--) { + char digit = static_cast(digits % 10); + + // String conversion step, straightforward mapping + retVal.push_back('0' + digit); + if (curDigit == 1) + retVal.push_back('.'); + + if (!digits) + // Short-circuit for precise representations + break; + } + + // Zeroes before the decimal: + if (power < 0) { + retVal.append(-power, '0'); + retVal.append(".0"); + } + + if (neg) + retVal.push_back('-'); + for ( + auto first = retVal.begin(), last = retVal.end(); + (first != last) && (first != --last); + ++first + ) + std::swap(*first, *last); + return retVal; + } + + void unmarshal(void* ptr, const char* szValue) const override { + T& value = *static_cast(ptr); + bool negative = *szValue == '-'; + if (negative) + szValue++; + + uint64_t whole = 0; + for (; *szValue; szValue++) { + // Detect the decimal marker, switch to fractional part + if (*szValue == '.') { + szValue++; + break; + } + if (*szValue < '0' || '9' < *szValue) + throw std::invalid_argument("String value is not a decimal number"); + whole = whole * 10 + *szValue - '0'; + } + + uint64_t fractional = 0; + size_t n = 0; + for (; szValue[n]; n++) { + if (szValue[n] < '0' || '9' < szValue[n]) + throw std::invalid_argument("String value is not a decimal number"); + fractional = fractional * 10 + szValue[n] - '0'; + } + value = static_cast(fractional); + while(n--) + value /= 10.0f; + value += static_cast(whole); + if (negative) + value *= -1.0f; + } + }; + + /// + /// Default marshaller, a point of specialization for external users + /// + template + struct marshaller : + builtin_marshaller + {}; + + /// + /// Nonprimitive or full specializations follow from here. Use these as examples when devising + /// your own marshallers. + /// + + template + struct marshaller> : + marshaller_base + { + typedef std::atomic type; + + // Marshaller for the interior type + marshaller interior; + + std::string marshal(const void* ptr) const override { + T value = static_cast(ptr)->load(); + return interior.marshal(&value); + } + + void unmarshal(void* ptr, const char* szValue) const override { + T value; + interior.unmarshal(&value, szValue); + *static_cast(ptr) = std::move(value); + } + }; + + template<> + struct marshaller : + marshaller_base + { + typedef std::string type; + + std::string marshal(const void* ptr) const override { + return *static_cast(ptr); + } + + void unmarshal(void* ptr, const char* szValue) const override { + *static_cast(ptr) = szValue; + } + }; +} diff --git a/src/autowiring/observable.h b/src/autowiring/observable.h index 24095ecaf..2b57c417e 100644 --- a/src/autowiring/observable.h +++ b/src/autowiring/observable.h @@ -1,6 +1,7 @@ // Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. #pragma once -#include "auto_signal.h" +#include "marshaller.h" +#include "signal.h" namespace autowiring { @@ -8,7 +9,7 @@ namespace autowiring { /// An unsynchronzied wrapper type that implements the observable pattern /// /// -/// +/// /// template class observable { @@ -38,6 +39,17 @@ class observable { public: operator const T&(void) const { return val; } const T& operator*(void) const { return val; } + const T& get(void) const { return val; } + + /// + /// Retrieves the underlying value, without any protection + /// + /// + /// Users should make use of this with caution. Changes to the returned value will not cause + /// signals to be asserted at the correct time by this type; users are responsible for doing + /// this on their own. + /// + T& get(void) { return val; } observable& operator=(const T& rhs) { onBeforeChanged(val, rhs); @@ -45,6 +57,34 @@ class observable { onChanged(); return *this; } + + observable& operator=(T&& rhs) { + onBeforeChanged(val, rhs); + val = std::move(rhs); + onChanged(); + return *this; + } +}; + + +template +struct marshaller> : + marshaller_base +{ + typedef autowiring::observable type; + + // Marshaller for the interior type + marshaller interior; + + std::string marshal(const void* ptr) const override { + return interior.marshal(&static_cast(ptr)->get()); + } + + void unmarshal(void* ptr, const char* szValue) const override { + T value; + interior.unmarshal(&value, szValue); + *static_cast(ptr) = std::move(value); + } }; } diff --git a/src/autowiring/once.h b/src/autowiring/once.h index da3b0745c..c09d70bde 100644 --- a/src/autowiring/once.h +++ b/src/autowiring/once.h @@ -4,6 +4,7 @@ #include "callable.h" #include "signal_base.h" #include "spin_lock.h" +#include #include namespace autowiring { diff --git a/src/autowiring/optional.h b/src/autowiring/optional.h new file mode 100644 index 000000000..2ffcf7743 --- /dev/null +++ b/src/autowiring/optional.h @@ -0,0 +1,145 @@ +// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. +#pragma once +#include +#include + +/// +/// Implements the "optional" concept +/// +/// +/// TODO: Replace this with a typedef to std::optional once the STL provides it +/// +namespace autowiring { +template +struct optional { +public: + optional(void) = default; + optional(const optional& rhs) = default; + optional(optional&& rhs) : + m_valid(rhs.m_valid) + { + if (rhs) { + new(val) T{ std::move(rhs._value()) }; + rhs._value().~T(); + rhs.m_valid = false; + } + } + optional(T&& value): + m_valid(true) + { + new(val) T{ std::move(value) }; + } + + ~optional(void) { + if (m_valid) + reinterpret_cast(val)->~T(); + } + +private: + bool m_valid = false; + uint8_t val[sizeof(T)]; + + T& _value(void) { return *reinterpret_cast(val); } + const T& _value(void) const { return *reinterpret_cast(val); } + +public: + operator bool(void) const { return m_valid; } + T& value(void) { + if (!m_valid) + throw std::runtime_error("Attempted to access an uninitialized optional value"); + return *reinterpret_cast(val); + } + const T& value(void) const { return const_cast(this)->value(); } + + // Indirection operators: + T* operator->(void) { return &value(); } + const T* operator->(void) const { return &value(); } + T& operator*(void) { return value(); } + const T& operator*(void) const { return value(); } + + // Comparisons + bool operator==(const optional& rhs) const { + return + (!m_valid && !rhs.m_valid) || + (m_valid && rhs.m_valid && _value() == rhs._value()); + } + bool operator==(const T& rhs) const { return m_valid && _value() == rhs; } + bool operator!=(const optional& rhs) const { return !(*this == rhs); } + bool operator!=(const T& rhs) const { return !(*this == rhs); } + + bool operator<(const optional& rhs) { + return + (!m_valid && !rhs.m_valid) || + (m_valid && rhs.m_valid && _value() < rhs._value()); + } + bool operator<(const T& rhs) { return m_valid && _value() < rhs; } + + // Standard assignment + optional& operator=(optional&& rhs) { + if (rhs.m_valid) + *this = std::move(rhs._value()); + else if (m_valid) { + // RHS is invalid, we need to invalidate ourselves + m_valid = false; + _value().~T(); + } + return *this; + } + optional& operator=(const optional& rhs) { + if (rhs.m_valid) + *this = rhs._value(); + else if(m_valid) { + m_valid = false; + _value().~T(); + } + return *this; + } + + template + typename std::enable_if< + !std::is_same::type, optional>::value, + optional + >::type& operator=(U&& rhs) { + if (m_valid) + _value() = std::forward(rhs); + else + new(val) T{ std::forward(rhs) }; + m_valid = true; + return *this; + } + + void swap(optional& rhs) { + if (m_valid) + if (rhs.m_valid) + std::swap(_value(), rhs._value()); + else { + new(rhs.val) T{ std::move(_value()) }; + rhs.m_valid = true; + m_valid = false; + _value().~T(); + } + else if (rhs.m_valid) { + new(val) T{ std::move(rhs._value()) }; + m_valid = true; + rhs.m_valid = false; + rhs._value().~T(); + } + } + + template + void emplace(Args&&... args) { + if (m_valid) + _value().~T(); + else + m_valid = true; + new(val) T{ args... }; + } +}; +} + +namespace std { + template + void swap(::autowiring::optional& lhs, ::autowiring::optional& rhs) { + lhs.swap(rhs); + } +} diff --git a/src/autowiring/signal.h b/src/autowiring/signal.h index 67301b14e..1e8c16dca 100644 --- a/src/autowiring/signal.h +++ b/src/autowiring/signal.h @@ -12,10 +12,7 @@ #include "spin_lock.h" #include #include -#include #include -#include -#include #include TYPE_TRAITS_HEADER /// diff --git a/src/autowiring/stdafx.h b/src/autowiring/stdafx.h index 40dd411f1..4a2a749d6 100644 --- a/src/autowiring/stdafx.h +++ b/src/autowiring/stdafx.h @@ -11,27 +11,18 @@ #define NOMINMAX #endif - #include #include #include #include - #include - #include #include - #include - #include #include #include #include #include - #include #include #include - #include #include - #include #include - #include #include #endif diff --git a/src/autowiring/test/AutoConfigTest.cpp b/src/autowiring/test/AutoConfigTest.cpp new file mode 100644 index 000000000..af869eab9 --- /dev/null +++ b/src/autowiring/test/AutoConfigTest.cpp @@ -0,0 +1,159 @@ +// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved. +#include "stdafx.h" +#include +#include +#include +#include + +class AutoConfigTest: + public testing::Test +{}; + +namespace { + class MyConfigurableClass { + public: + autowiring::config a{ "Hello world!" }; + std::atomic b{ 929 }; + std::atomic bUnsigned{ 92999 }; + volatile bool c = false; + volatile float d = 1.0f; + volatile double e = 99.2; + autowiring::observable obs{ 44 }; + autowiring::config cfg{ 929999 }; + + static autowiring::config_descriptor GetConfigDescriptor(void) { + return { + { "a", "Field A description", &MyConfigurableClass::a, "Hello world!" }, + { "b", "Field B description", &MyConfigurableClass::b, 929 }, + { "bUnsigned", &MyConfigurableClass::bUnsigned }, + { "c", &MyConfigurableClass::c }, + { "d", &MyConfigurableClass::d }, + { "e", &MyConfigurableClass::e }, + { "obs", &MyConfigurableClass::obs }, + { "cfg", &MyConfigurableClass::cfg } + }; + } + }; + + class BadClass { + public: + autowiring::config_descriptor GetConfigDescriptor(void) { return{}; } + }; +} + +static_assert(autowiring::has_getconfigdescriptor::value, "Static new not correctly detected"); +static_assert(!autowiring::has_getconfigdescriptor::value, "Bad class cannot have a configuration descriptor"); + +TEST_F(AutoConfigTest, ConfigFieldAssign) { + autowiring::config x{ "Hello world!" }; + ASSERT_TRUE(x.is_dirty()) << "Config values are assumed to be initially dirty"; +} + +TEST_F(AutoConfigTest, String) { + MyConfigurableClass c; + + autowiring::config_field cf("a", "Field A description", &MyConfigurableClass::a, std::string{ "Hello world!" }); + + std::string expected{ "Forescore and seven years ago" }; + autowiring::ConfigSet("a", c, expected.c_str()); + ASSERT_TRUE(c.a.clear_dirty()); + ASSERT_STREQ(expected.c_str(), c.a->c_str()) << "String configuration value not assigned"; +} + +TEST_F(AutoConfigTest, Integer) { + MyConfigurableClass c; + + autowiring::ConfigSet("b", c, "999"); + ASSERT_EQ(c.b, 999) << "Integer configuration value not assigned"; + + autowiring::ConfigSet("b", c, "-999"); + ASSERT_EQ(c.b, -999) << "Negative integer configuration value not assigned"; +} + +TEST_F(AutoConfigTest, IntegerUnsigned) { + MyConfigurableClass c; + + c.bUnsigned = 10929; + std::string strVal = autowiring::ConfigGet("bUnsigned", c); + ASSERT_STREQ("10929", strVal.c_str()); + + autowiring::ConfigSet("bUnsigned", c, "999"); + ASSERT_EQ(c.bUnsigned, 999) << "Integer configuration value not assigned"; + ASSERT_ANY_THROW(autowiring::ConfigSet("bUnsigned", c, "-999")) << "Incorrectly assigned as signed value to an unsigned field"; +} + +TEST_F(AutoConfigTest, Bool) { + MyConfigurableClass c; + + autowiring::ConfigSet("c", c, "true"); + ASSERT_TRUE(c.c) << "Boolean configuration value not assigned"; +} + +TEST_F(AutoConfigTest, Float) { + MyConfigurableClass c; + + c.d = 10929.4f; + std::string strVal = autowiring::ConfigGet("d", c); + ASSERT_STREQ("10929.4", strVal.c_str()); + + autowiring::ConfigSet("d", c, "123.4"); + ASSERT_FLOAT_EQ(c.d, 123.4f) << "Float configuration value not assigned"; +} + +TEST_F(AutoConfigTest, Double) { + MyConfigurableClass c; + std::string strVal; + + c.e = 10929.4423; + strVal = autowiring::ConfigGet("e", c); + ASSERT_STREQ("10929.4423", strVal.c_str()); + + c.e = 109290000000000; + strVal = autowiring::ConfigGet("e", c); + ASSERT_STREQ("109290000000000", strVal.c_str()); + + c.e = -0.0099291; + strVal = autowiring::ConfigGet("e", c); + ASSERT_STREQ("-0.0099291", strVal.c_str()); + + autowiring::ConfigSet("e", c, "77482.4"); + ASSERT_DOUBLE_EQ(c.e, 77482.4) << "Double configuration value not assigned"; +} + +TEST_F(AutoConfigTest, Observable) { + MyConfigurableClass c; + + bool hit = false; + c.obs.onChanged += [&hit] { hit = true; }; + autowiring::ConfigSet("obs", c, "101"); + ASSERT_EQ(101, c.obs) << "Value not properly assigned"; + ASSERT_TRUE(hit) << "Observable signal not asserted"; +} + +TEST_F(AutoConfigTest, Configurable) { + AutoCurrentContext ctxt; + AutoRequired c; + + ASSERT_TRUE(c->cfg.clear_dirty()) << "Dirty flag was not set on a value that should have been dirty"; + ASSERT_FALSE(c->cfg.is_dirty()); + ctxt->Config.Set("cfg", "888"); + ASSERT_TRUE(c->cfg.is_dirty()) << "Dirty flag not set correctly after being updated in configuration"; + ASSERT_TRUE(c->cfg.clear_dirty()); + ASSERT_EQ(888, c->cfg); +} + +TEST_F(AutoConfigTest, ContextSetSimple) { + AutoCurrentContext ctxt; + AutoRequired mcc; + + ctxt->Config.Set("b", "1029"); + ASSERT_EQ(mcc->b, 1029); +} + +TEST_F(AutoConfigTest, ContextSetAfter) { + AutoCurrentContext ctxt; + ctxt->Config.Set("b", "10442"); + + AutoRequired mcc; + ASSERT_EQ(mcc->b, 10442); +} diff --git a/src/autowiring/test/CMakeLists.txt b/src/autowiring/test/CMakeLists.txt index ae16ce2c6..4f9b29100 100644 --- a/src/autowiring/test/CMakeLists.txt +++ b/src/autowiring/test/CMakeLists.txt @@ -2,6 +2,7 @@ set(AutowiringTest_SRCS AnySharedPointerTest.cpp ArgumentTypeTest.cpp AtomicListTest.cpp + AutoConfigTest.cpp AutoConstructTest.cpp AutoFilterAltitudeTest.cpp AutoFilterCollapseRulesTest.cpp