You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
217 lines
8.5 KiB
217 lines
8.5 KiB
// Copyright 2014 The Chromium OS Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// This is an implementation of a "true" variant class in C++.
|
|
// The brillo::Any class can hold any C++ type, but both the setter and
|
|
// getter sites need to know the actual type of data.
|
|
// Note that C-style arrays when stored in Any are reduced to simple
|
|
// data pointers. Any will not copy a contents of the array.
|
|
// const int data[] = [1,2,3];
|
|
// Any v(data); // stores const int*, effectively "Any v(&data[0]);"
|
|
|
|
// brillo::Any is a value type. Which means, the data is copied into it
|
|
// and Any owns it. The owned object (stored by value) will be destroyed
|
|
// when Any is cleared or reassigned. The contained value type must be
|
|
// copy-constructible. You can also store pointers and references to objects.
|
|
// Storing pointers is trivial. In order to store a reference, you can
|
|
// use helper functions std::ref() and std::cref() to create non-const and
|
|
// const references respectively. In such a case, the type of contained data
|
|
// will be std::reference_wrapper<T>. See 'References' unit tests in
|
|
// any_test.cc for examples.
|
|
|
|
#ifndef LIBBRILLO_BRILLO_ANY_H_
|
|
#define LIBBRILLO_BRILLO_ANY_H_
|
|
|
|
#include <brillo/any_internal_impl.h>
|
|
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include <brillo/brillo_export.h>
|
|
#include <brillo/type_name_undecorate.h>
|
|
|
|
namespace dbus {
|
|
class MessageWriter;
|
|
} // namespace dbus
|
|
|
|
namespace brillo {
|
|
|
|
class BRILLO_EXPORT Any final {
|
|
public:
|
|
Any(); // Do not inline to hide internal_details::Buffer from export table.
|
|
// Standard copy/move constructors. This is a value-class container
|
|
// that must be copy-constructible and movable. The copy constructors
|
|
// should not be marked as explicit.
|
|
Any(const Any& rhs);
|
|
Any(Any&& rhs); // NOLINT(build/c++11)
|
|
// Typed constructor that stores a value of type T in the Any.
|
|
template<class T>
|
|
inline Any(T value) { // NOLINT(runtime/explicit)
|
|
data_buffer_.Assign(std::move(value));
|
|
}
|
|
|
|
// Not declaring the destructor as virtual since this is a sealed class
|
|
// and there is no need to introduce a virtual table to it.
|
|
~Any();
|
|
|
|
// Assignment operators.
|
|
Any& operator=(const Any& rhs);
|
|
Any& operator=(Any&& rhs); // NOLINT(build/c++11)
|
|
template<class T>
|
|
inline Any& operator=(T value) {
|
|
data_buffer_.Assign(std::move(value));
|
|
return *this;
|
|
}
|
|
|
|
// Compares the contents of two Any objects for equality. Note that the
|
|
// contained type must be equality-comparable (must have operator== defined).
|
|
// If operator==() is not available for contained type, comparison operation
|
|
// always returns false (as if the data were different).
|
|
bool operator==(const Any& rhs) const;
|
|
inline bool operator!=(const Any& rhs) const { return !operator==(rhs); }
|
|
|
|
// Checks if the given type DestType can be obtained from the Any.
|
|
// For example, to check if Any has a 'double' value in it:
|
|
// any.IsTypeCompatible<double>()
|
|
template<typename DestType>
|
|
bool IsTypeCompatible() const {
|
|
// Make sure the requested type DestType conforms to the storage
|
|
// requirements of Any. We always store the data by value, which means we
|
|
// strip away any references as well as cv-qualifiers. So, if the user
|
|
// stores "const int&", we actually store just an "int".
|
|
// When calling IsTypeCompatible, we need to do a similar "type cleansing"
|
|
// to make sure the requested type matches the type of data actually stored,
|
|
// so this "canonical" type is used for type checking below.
|
|
using CanonicalDestType = typename std::decay<DestType>::type;
|
|
const char* contained_type = GetTypeTagInternal();
|
|
if (strcmp(GetTypeTag<CanonicalDestType>(), contained_type) == 0)
|
|
return true;
|
|
|
|
if (!std::is_pointer<CanonicalDestType>::value)
|
|
return false;
|
|
|
|
// If asking for a const pointer from a variant containing non-const
|
|
// pointer, still satisfy the request. So, we need to remove the pointer
|
|
// specification first, then strip the const/volatile qualifiers, then
|
|
// re-add the pointer back, so "const int*" would become "int*".
|
|
using NonPointer = typename std::remove_pointer<CanonicalDestType>::type;
|
|
using CanonicalDestTypeNoConst = typename std::add_pointer<
|
|
typename std::remove_const<NonPointer>::type>::type;
|
|
if (strcmp(GetTypeTag<CanonicalDestTypeNoConst>(), contained_type) == 0)
|
|
return true;
|
|
|
|
using CanonicalDestTypeNoVolatile = typename std::add_pointer<
|
|
typename std::remove_volatile<NonPointer>::type>::type;
|
|
if (strcmp(GetTypeTag<CanonicalDestTypeNoVolatile>(), contained_type) == 0)
|
|
return true;
|
|
|
|
using CanonicalDestTypeNoConstOrVolatile = typename std::add_pointer<
|
|
typename std::remove_cv<NonPointer>::type>::type;
|
|
return strcmp(GetTypeTag<CanonicalDestTypeNoConstOrVolatile>(),
|
|
contained_type) == 0;
|
|
}
|
|
|
|
// Returns immutable data contained in Any.
|
|
// Aborts if Any doesn't contain a value of type T, or trivially
|
|
// convertible to/compatible with it.
|
|
template<typename T>
|
|
const T& Get() const {
|
|
CHECK(IsTypeCompatible<T>())
|
|
<< "Requesting value of type '" << brillo::GetUndecoratedTypeName<T>()
|
|
<< "' from variant containing '" << GetUndecoratedTypeName()
|
|
<< "'";
|
|
return data_buffer_.GetData<T>();
|
|
}
|
|
|
|
// Returns a copy of data in Any and returns true when that data is
|
|
// compatible with T. Returns false if contained data is incompatible.
|
|
template<typename T>
|
|
bool GetValue(T* value) const {
|
|
if (!IsTypeCompatible<T>()) {
|
|
return false;
|
|
}
|
|
*value = Get<T>();
|
|
return true;
|
|
}
|
|
|
|
// Returns a pointer to mutable value of type T contained within Any.
|
|
// No data copying is made, the data pointed to is still owned by Any.
|
|
// If Any doesn't contain a value of type T, or trivially
|
|
// convertible/compatible to/with it, then it returns nullptr.
|
|
template<typename T>
|
|
T* GetPtr() {
|
|
if (!IsTypeCompatible<T>())
|
|
return nullptr;
|
|
return &(data_buffer_.GetData<T>());
|
|
}
|
|
|
|
// Returns a copy of the data contained in Any.
|
|
// If the Any doesn't contain a compatible value, the provided default
|
|
// |def_val| is returned instead.
|
|
template<typename T>
|
|
T TryGet(typename std::decay<T>::type const& def_val) const {
|
|
if (!IsTypeCompatible<T>())
|
|
return def_val;
|
|
return data_buffer_.GetData<T>();
|
|
}
|
|
|
|
// A convenience specialization of the above function where the default
|
|
// value of type T is returned in case the underlying Get() fails.
|
|
template<typename T>
|
|
T TryGet() const {
|
|
return TryGet<T>(typename std::decay<T>::type());
|
|
}
|
|
|
|
// Returns the undecorated name of the type contained within Any.
|
|
inline std::string GetUndecoratedTypeName() const {
|
|
return GetUndecoratedTypeNameForTag(GetTypeTagInternal());
|
|
}
|
|
// Swaps the value of this object with that of |other|.
|
|
void Swap(Any& other);
|
|
// Checks if Any is empty, that is, not containing a value of any type.
|
|
bool IsEmpty() const;
|
|
// Clears the Any and destroys any contained object. Makes it empty.
|
|
void Clear();
|
|
// Checks if Any contains a type convertible to integer.
|
|
// Any type that match std::is_integral<T> and std::is_enum<T> is accepted.
|
|
// That includes signed and unsigned char, short, int, long, etc as well as
|
|
// 'bool' and enumerated types.
|
|
// For 'integer' type, you can call GetAsInteger to do implicit type
|
|
// conversion to intmax_t.
|
|
bool IsConvertibleToInteger() const;
|
|
// For integral types and enums contained in the Any, get the integer value
|
|
// of data. This is a useful function to obtain an integer value when
|
|
// any can possibly have unspecified integer, such as 'short', 'unsigned long'
|
|
// and so on.
|
|
intmax_t GetAsInteger() const;
|
|
// Writes the contained data to D-Bus message writer, if the appropriate
|
|
// serialization method for contained data of the given type is provided
|
|
// (an appropriate specialization of AppendValueToWriter<T>() is available).
|
|
// Returns false if the Any is empty or if there is no serialization method
|
|
// defined for the contained data.
|
|
void AppendToDBusMessageWriter(::dbus::MessageWriter* writer) const;
|
|
|
|
private:
|
|
// Returns a pointer to a static buffer containing type tag (sort of a type
|
|
// name) of the contained value.
|
|
const char* GetTypeTagInternal() const;
|
|
|
|
// The data buffer for contained object.
|
|
internal_details::Buffer data_buffer_;
|
|
};
|
|
|
|
} // namespace brillo
|
|
|
|
namespace std {
|
|
|
|
// Specialize std::swap() algorithm for brillo::Any class.
|
|
inline void swap(brillo::Any& lhs, brillo::Any& rhs) {
|
|
lhs.Swap(rhs);
|
|
}
|
|
|
|
} // namespace std
|
|
|
|
#endif // LIBBRILLO_BRILLO_ANY_H_
|