Skip to content

Commit

Permalink
Better non-owning any implementation (#4045)
Browse files Browse the repository at this point in the history
  • Loading branch information
lisitsyn committed Dec 24, 2017
1 parent 7e693f2 commit c7af75e
Show file tree
Hide file tree
Showing 2 changed files with 241 additions and 45 deletions.
129 changes: 96 additions & 33 deletions src/shogun/lib/any.h
Expand Up @@ -107,18 +107,19 @@ namespace shogun
*/
virtual bool matches(const std::type_info& ti) const = 0;

/** Checks if policies are compatible.
* @param other other policy
* @return true if policies do match
*/
virtual bool matches(BaseAnyPolicy* other) const = 0;

/** Compares two storages.
* @param storage pointer to a pointer to storage
* @param other_storage pointer to a pointer to another storage
* @return true if both storages have same value
*/
virtual bool equals(void** storage, void** other_storage) const = 0;

/** Returns the name of policy.
* @return name of policy
*/
virtual std::string policy_name() const = 0;

/** Returns the type of policy.
* @return type of policy
*/
Expand Down Expand Up @@ -174,6 +175,12 @@ namespace shogun
return typeid(T) == ti;
}

/** Checks if policies are compatible.
* @param other other policy
* @return true if policies do match
*/
virtual bool matches(BaseAnyPolicy* other) const;

/** Compares two storages.
* @param storage pointer to a pointer to storage
* @param other_storage pointer to a pointer to another storage
Expand All @@ -186,11 +193,6 @@ namespace shogun
return typed_storage == typed_other_storage;
}

virtual std::string policy_name() const
{
return "owning";
}

virtual PolicyType policy_type() const
{
return PolicyType::OWNING;
Expand All @@ -207,7 +209,7 @@ namespace shogun
*/
virtual void set(void** storage, const void* v) const
{
*(storage) = const_cast<void*>(v);
*static_cast<T*>(*(storage)) = T(*reinterpret_cast<T const*>(v));
}

/** Clears storage.
Expand Down Expand Up @@ -242,6 +244,12 @@ namespace shogun
return typeid(T) == ti;
}

/** Checks if policies are compatible.
* @param other other policy
* @return true if policies do match
*/
virtual bool matches(BaseAnyPolicy* other) const;

/** Compares two storages.
* @param storage pointer to a pointer to storage
* @param other_storage pointer to a pointer to another storage
Expand All @@ -254,11 +262,6 @@ namespace shogun
return typed_storage == typed_other_storage;
}

virtual std::string policy_name() const
{
return "non owning";
}

virtual PolicyType policy_type() const
{
return PolicyType::NON_OWNING;
Expand All @@ -281,6 +284,34 @@ namespace shogun
return &policy;
}

template <class T>
bool NonOwningAnyPolicy<T>::matches(BaseAnyPolicy* other) const
{
if (this == other)
{
return true;
}
if (other == owning_policy<T>())
{
return true;
}
return matches(other->type_info());
}

template <class T>
bool PointerValueAnyPolicy<T>::matches(BaseAnyPolicy* other) const
{
if (this == other)
{
return true;
}
if (other == non_owning_policy<T>())
{
return true;
}
return matches(other->type_info());
}

/** @brief Allows to store objects of arbitrary types
* by using a BaseAnyPolicy and provides a type agnostic API.
* See its usage in CSGObject::Self, CSGObject::set(), CSGObject::get()
Expand All @@ -298,24 +329,29 @@ namespace shogun
{
}

/** Base constructor */
Any(BaseAnyPolicy* the_policy, void* the_storage)
: policy(the_policy), storage(the_storage)
{
}

/** Constructor to copy value */
template <typename T>
explicit Any(const T& v) : Any(owning_policy<T>(), nullptr)
{
policy->set(&storage, &v);
}

/** Base constructor */
Any(BaseAnyPolicy* the_policy, void* the_storage)
: policy(the_policy), storage(the_storage)
/** Copy constructor */
Any(const Any& other) : Any(other.policy, nullptr)
{
set_or_inherit(other);
}

/** Copy constructor */
Any(const Any& other) : Any(other.policy, nullptr)
/** Move constructor */
Any(Any&& other) : Any(other.policy, nullptr)
{
assert_same_policy_type(other.policy);
policy->set(&storage, other.storage);
set_or_inherit(other);
}

/** Assignment operator
Expand All @@ -324,10 +360,28 @@ namespace shogun
*/
Any& operator=(const Any& other)
{
assert_same_policy_type(other.policy);
if (empty())
{
policy = other.policy;
set_or_inherit(other);
return *(this);
}
if (!policy->matches(other.policy))
{
throw std::logic_error(
"Bad assign into " + policy->type() + " from " +
other.policy->type());
}
policy->clear(&storage);
policy = other.policy;
policy->set(&storage, other.storage);
if (other.policy->policy_type() == PolicyType::NON_OWNING)
{
policy = other.policy;
storage = other.storage;
}
else
{
policy->set(&storage, other.storage);
}
return *(this);
}

Expand Down Expand Up @@ -397,13 +451,15 @@ namespace shogun
}

private:
void assert_same_policy_type(BaseAnyPolicy* other_policy)
void set_or_inherit(const Any& other)
{
if (policy->policy_type() != other_policy->policy_type())
if (other.policy->policy_type() == PolicyType::NON_OWNING)
{
throw std::logic_error(
"The policies are different: " + policy->policy_name() +
" and " + other_policy->policy_name());
storage = other.storage;
}
else
{
policy->set(&storage, other.storage);
}
}

Expand All @@ -414,10 +470,17 @@ namespace shogun

inline bool operator==(const Any& lhs, const Any& rhs)
{
if (lhs.empty() || rhs.empty())
{
return lhs.empty() && rhs.empty();
}
if (!lhs.policy->matches(rhs.policy))
{
return false;
}
void* lhs_storage = lhs.storage;
void* rhs_storage = rhs.storage;
return lhs.policy == rhs.policy &&
lhs.policy->equals(&lhs_storage, &rhs_storage);
return lhs.policy->equals(&lhs_storage, &rhs_storage);
}

inline bool operator!=(const Any& lhs, const Any& rhs)
Expand Down

0 comments on commit c7af75e

Please sign in to comment.