Package
Запаковщик данных на основе C++11 метафункций.
Classes
Referencing classes package_const_reference and package_reference define wrappers over plain pointers to an externally provided buffer. The wrappers allow manipulations over packaged data and access to it. They are constructed in either of two ways: from packed data pointed to by an externally provided pointer, or from unpacked data which is packed and written to an externally provided buffer. In both cases the buffers are not owned by the wrappers, and a user is responsible to manage the memory.
package_const_reference
Defines a reference to a provided memory buffer to contain a package of objects of types T... given by the template parameter pack in order, in which the types appear in the template instantiation parameter list. The memory provided to the wrapper is read-only accessed by the wrapper itself, and by exposed methods, other than the packing constructors which fill the buffer.
template <class...T>
class package_const_reference
{
public:
typedef typename std::common_type<typename package_const_reference<T>::size_type...>::type size_type;
template <size_type I>
using packed_value_type; /*package_const_reference<T0, T1, ..., Ti, ...>::template packed_value_type<i> resolves into package_const_reference<Ti>*/
template <size_type I>
using packed_type_from; /*package_const_reference<T0, T1, ..., Ti,...>::template packed_value_type<i> resolves into package_const_reference<Ti...>*/
template <size_type I>
using value_type = typename packed_value_type<I>::value_type; /*package_const_reference<T0, T1, ..., Ti,...>::template value_type<i> resolves into typename packing_service_provider<Ti>::value_type*/
/*which is typename std::remove_cv<typename std::remove_reference<Ti>::type>::type by default*/
template <class...U>
static size_type calculate_size(const U&...refs);
static _Success_(return != false) bool verify_package_size(_In_bytecount_(cbBuf) const void* pBuf, size_type cbBuf, _Out_opt_ size_type* pSize = nullptr);
package_const_reference();
template <class...U>
package_const_reference(_Out_bytecap_(cbPackedData) void* pPackedData, size_type cbPackedData, _In_ U&&...refs);
package_const_reference(_In_bytecount_(cbPackedData) const void* pPackedData, size_type cbPackedData);
package_const_reference(const package_reference<T0, TN...>& refRight);
const void* data() const;
const void* endptr() const;
size_type size() const;
/*specifics*/
/*Improved readability can be achived by using template type aliases above - for compiling using gcc or MSVC of the 2015 version*/
template <size_type packed_pos, class...get_params_t>
typename packed_item<packed_pos, T0, TN...>::const_packed_item_ref_type::const_get_type get(get_params_t&&...get_params) const;
template <size_type packed_pos>
typename packed_item<packed_pos, T0, TN...>::const_packed_item_ref_type get_packed_item() const;
template<size_type packed_start_pos>
typename packed_item<packed_start_pos, T0, TN...>::const_packed_with_rest_ref_type get_packed_items_from() const;
};
template <class T>
class package_const_reference<T>:
private packing_service_provider<typename std::remove_cv<typename std::remove_reference<T>::type>::type>,
public package_const_interface_provider<
typename std::remove_cv<typename std::remove_reference<T>::type>::type,
package_const_reference<typename std::remove_cv<typename std::remove_reference<T>::type>::type>
>
{
public:
/*The following types are specified by the service provider*/
typedef typename sp_t::value_type value_type;
typedef typename sp_t::const_reference reference, const_reference;
typedef typename sp_t::const_pointer pointer, const_pointer;
typedef typename sp_t::const_get_type get_type, const_get_type;
typedef typename sp_t::size_type size_type;
template <class U>
static size_type calculate_size(U&& ref);
static _Success_(return != false) bool verify_package_size(_In_bytecount_(cbBuf) const void* pBuf, size_type cbBuf, _Out_opt_ size_type* pSize = nullptr);
package_const_reference();
template <class U>
package_const_reference(_Out_bytecap_(cbPackedData) void* pPackedData, size_type cbPackedData, _In_ U&& ref);
package_const_reference(_In_bytecount_(cbPackedData) const void* pPackedData, size_type cbPackedData);
package_const_reference(const package_reference<T>& ref);
const void* data() const;
const void* endptr() const;
size_type size() const;
template <class...get_params_t>
const_get_type get(get_params_t&&...get_params) const;
};
package_reference
Defines a reference to a package data stored in a provided buffer, but the package is considered mutable, that is methods provided by the wrapper, allow writing into the buffer.
template <class...T>
class package_reference
{
public:
typedef typename std::common_type<package_reference<T>::size_type...>::type size_type;
template <size_type I>
using packed_value_type; /*package_reference<T0, T1, ..., Ti, ...>::template packed_value_type<i> resolves into package_reference<Ti>*/
template <size_type I>
using packed_type_from; /*package_reference<T0, T1, ..., Ti,...>::template packed_value_type<i> resolves into package_reference<Ti...>*/
template <size_type I>
using value_type = typename packed_value_type<I>::value_type; /*package_reference<T0, T1, ..., Ti,...>::template value_type<i> resolves into typename packing_service_provider<Ti>::value_type*/
/*which is typename std::remove_cv<typename std::remove_reference<Ti>::type>::type by default*/
template <class...U>
static size_type calculate_size(const U&...refs);
static _Success_(return != false) bool verify_package_size(_In_bytecount_(cbBuf) const void* pBuf, size_type cbBuf, _Out_opt_ size_type* pSize = nullptr);
package_reference();
template <class...U>
package_reference(_Out_bytecap_(cbPackedData) void* pPackedData, size_type cbPackedData, _In_ U&&...refs);
package_reference(_In_bytecount_(cbPackedData) void* pPackedData, size_type cbPackedData);
const void* data() const;
void* data();
const void* endptr() const;
void* endptr();
size_type size() const;
/*specifics*/
template <size_type packed_pos, class...get_params_t>
typename packed_item<packed_pos, T0, TN...>::packed_item_ref_type::const_get_type get(get_params_t&&...get_params) const;
template <size_type packed_pos, class...get_params_t>
typename packed_item<packed_pos, T0, TN...>::packed_item_ref_type::get_type get(get_params_t&&...get_params);
template <size_type packed_pos>
typename packed_item<packed_pos, T0, TN...>::const_packed_item_ref_type get_packed_item() const;
template <size_type packed_pos>
typename packed_item<packed_pos, T0, TN...>::packed_item_ref_type get_packed_item();
template<size_type packed_start_pos>
typename packed_item<packed_start_pos, T0, TN...>::const_packed_with_rest_ref_type get_packed_items_from() const;
template<size_type packed_start_pos>
typename packed_item<packed_start_pos, T0, TN...>::packed_with_rest_ref_type get_packed_items_from();
};
template <class T>
class package_reference<T>:
private packing_service_provider<typename std::remove_cv<typename std::remove_reference<T>::type>::type>,
public package_interface_provider<
typename std::remove_cv<typename std::remove_reference<T>::type>::type,
package_reference<typename std::remove_cv<typename std::remove_reference<T>::type>::type>
>
{
public:
/*The following types are specified by the service provider*/
typedef typename sp_t::value_type value_type;
typedef typename sp_t::reference reference;
typedef typename sp_t::const_reference const_reference;
typedef typename sp_t::pointer pointer;
typedef typename sp_t::const_pointer const_pointer;
typedef typename sp_t::const_get_type const_get_type;
typedef typename sp_t::get_type get_type;
typedef typename sp_t::size_type size_type;
template <class U>
static size_type calculate_size(U&& ref) {return sp_t::calculate_size(std::forward<U>(ref));}
static _Success_(return != false) bool verify_package_size(_In_bytecount_(cbBuf) const void* pBuf, size_type cbBuf, _Out_opt_ size_type* pSize = nullptr);
package_reference();
template <class U>
package_reference(_Out_bytecap_(cbPackedData) void* pPackedData, size_type cbPackedData, _In_ U&& ref);
package_reference(_In_bytecount_(cbPackedData) void* pPackedData, size_type cbPackedData);
const void* data() const;
void* data();
const void* endptr() const;
void* endptr();
size_type size() const;
template <class...get_params_t>
const_get_type get(get_params_t&&...get_params) const;
template <class...get_params_t>
get_type get(get_params_t&&...get_params);
};
const_package and package
Allocating classes const_package and package define wrappers over packages, memory for which is managed by the wrappers, and not externally. The wrappers define the same package access interface as the referencing wrappers, except for packing constructors which do not require external buffer to manage, but allocate their own buffer using externally provided allocator object. Also, they contain allocator retrieving method get_allocator and define allocator_type type alias for the allocator they use. Additionally, any copying of two allocating wrappers involve copying the entire package.
template <class alloc_t, class...types_to_pack>
class const_package:public package_const_reference<types_to_pack...>
{
public:
typedef typename std::allocator_traits<alloc_t>::template rebind_alloc<std::uint8_t> allocator_type;
const_package() = default;
template <class...pack_input_t>
const_package(std::allocator_arg_t, const allocator_type& alloc, pack_input_t&&...data_to_pack);
template <class...pack_input_t>
const_package(pack_input_t&&...data_to_pack);
const_package(_In_bytecount_(cbPackedData) const void* pPackedData, size_type cbPackedData, const allocator_type& alloc = allocator_type());
~const_package();
const_package(const const_package& ref);
const_package(const_package&& ref);
const_package& operator=(const const_package& right);
const_package& operator=(const_package&& right);
allocator_type get_allocator() const;
void* release();
};
template <class alloc_t, class...types_to_pack>
class package:public package_reference<types_to_pack...>
{
public:
typedef typename std::allocator_traits<alloc_t>::template rebind_alloc<std::uint8_t> allocator_type;
package() = default;
template <class...pack_input_t>
package(std::allocator_arg_t, const allocator_type& alloc, pack_input_t&&...data_to_pack);
template <class...pack_input_t>
package(pack_input_t&&...data_to_pack);
package(_In_bytecount_(cbPackedData) const void* pPackedData, size_type cbPackedData, const allocator_type& alloc = allocator_type());
~package();
package(const package& ref);
package(package&& ref);
package& operator=(const package& right);
package& operator=(package&& right);
allocator_type get_allocator() const;
void* release();
};
The classes are built to be easily customizable for types of any complexity and usable.
The marshallers require size_type to be visible in the scope.
Customization
The customization is performed by either specialization of referencing classes for a given type (the hard way), or by specializations of packaging service provider (see packing_service_provider class template) and interface provider (package_const_interface_provider and package_interface_provider) for that type (the easy way).
The service providers define a basic set of static methods called by the marshallers, which include package size calculation and verification for packed and unpacked data, packing process, accessing to package data. Also, the provider defines basic types used by the marshallers. See default_packing_service_provider for the full list of the required methods and types.
The interface providers for type T are openly inherited by the default wrappers for that type and as such provide interface methods to be exposed by the wrappers. The default interface provider (unspecialized package_const_interface_provider<T, wrapper_type> template) has an empty body.
By default, the service and interface providers treat any object of type T as a plain block of data, which can be suitable for POD types, but requires redefinition for types of some complexity, including dynamically polymorphic types. For the purposes of default packaging default_packing_service_provider<T> is introduced.
Customization of providers. The packing_service_provider<T> template defines semantics of a package for type T objects. By default it openly inherits the default_packing_service_provider and uses its methods which treat the objects as plain data blocks of size sizeof(T). To define different behaviour specialize the template for any needed type. The specialization must provide the following types and methods.
Types
- value_type
- is a type to be used as an input for a marshalling method. If the marshaller is specialized for a given type T (e.g. package<T>), than T must be implicitly convertible to packing_service_provider<T>::value_type. Typical definition of value_type is T itself.
- reference
- is a mutable reference to an object being packed.
- const_reference
- is a constant reference to an object being packed.
- pointer
- is a pointer to a mutable object being packed.
- const_pointer
- is a pointer to a constant object being packed.
- get_type
- is a type of data to be returned to a user of a marshaller when package is being read. The returned data is considered mutable.
- const_get_type
- is a type of data to be returned to a user of a marshaller when package is being read. The returned data is considered constant.
- size_type
- is a type of byte size of
Methods
1) template <class U> static size_type calculate_size(U&& object_to_pack);
is a function returning a byte size of a package that would be created if an object retrieved from an implicit conversion of object_to_pack to type value_type was packed. The parameter can be ignored by the function implementation if the size is known beforehand.
2) static bool verify_package_size(_In_bytecount_(cbBuf) const void* pBuf, size_type cbBuf, _Out_opt_ size_type* pSize = nullptr);
is a function which receives a buffer pBuf of size cbBuf with a package, checks if the buffer data is of sufficient size to completely described a packed object, and, if the check succeeds returns true, or false otherwise. Also, if the size check succeeds and pSize is not a null pointer, on output the function writes an actual size of the package within the buffer to pSize.
3) template <class U> static size_type write(_Out_bytecap_(cbPackedData) void* pPackedData, size_type cbPackedData, _In_ U&& ref);
is a packing function which recieves some uninitialized buffer pPackedData of byte size cbData and an object ref, implicitly convertible to value_type, and packs the object writing the package to the buffer.
4) static size_type size(_In_bytecount_(cbBuf) const void* pBuf, size_type cbBuf);
is a function parsing a given package to retrieve its size.
5) template <class...constr_params_t> static get_type read(_In_bytecount_(cbBuf) void* pBuf, size_type cbBuf, constr_params_t&&...constr_params);
is a function reading a specified package to return an unpacked object of type get_type to the user of the corresponding wrapper. constr_params are forwarded to the provider via packagers' get methods.
6) template <class...constr_params_t> static const_get_type read(_In_bytecount_(cbBuf) const void* pBuf, size_type cbBuf, constr_params_t&&...constr_params);
is a similar function returning a constant object of type const_get_type to a user of the wrapper.
Inheriting from the default service provider with redefining chosen functions is allowed.
Example - Redefining marshaller service provider for a polymorphic type which implements an access to a built in POD type:
struct I
{
virtual int getval() const = 0;
};
struct C:I
{
int x;
C(int arg):x(arg) {}
int getval() const
{
return x;
}
};
//Redefinition
template <>
struct packing_service_provider<I>:default_packing_service_provider<int>
{
typedef std::unique_ptr<I> get_type;
typedef std::unique_ptr<const I> const_get_type;
typedef std::uint32_t size_type;
static size_type write(void* pOut, size_type cbOut, const I& obj)
{
return default_packing_service_provider<int>::write(pOut, cbOut, obj.getval());
}
static get_type read(_In_bytecount_(cbBuf) void* pBuf, size_type cbBuf)
{
return get_type(new C(default_packing_service_provider<int>::read(pBuf, cbBuf)));
}
static const_get_type read(_In_bytecount_(cbBuf) const void* pBuf, size_type cbBuf)
{
return get_type(new C(default_packing_service_provider<int>::read(pBuf, cbBuf)));
}
static get_type read(_In_bytecount_(cbBuf) void* pBuf, size_type cbBuf, int init)
{
return get_type(new C(default_packing_service_provider<int>::read(pBuf, cbBuf) + init));
}
static const_get_type read(_In_bytecount_(cbBuf) const void* pBuf, size_type cbBuf, int init)
{
return get_type(new C(default_packing_service_provider<int>::read(pBuf, cbBuf) + init));
}
};
//usage
#include <iostream>
int main(int, char**)
{
C val = 0;
auto pckg = CAMaaS::make_package(static_cast<I&>(val));
std::cout << pckg.get()->getval() << '\n';
std::cout << pckg.get(1)->getval() << '\n';
return 0;
}
Interface providers
Interface providers for a marshaller for objects of a type T are specializations of the package_interface_provider<T, interface_class>, where interface_class is a type provided by the marshaller. The specialization is openly inherited by the marshaller, so that publicly available members of the interface provider class are exposed by the marshaller. In order to get an access to the marshaller one can statically cast a pointer to the interface provider to the interface_class* pointer type.
Default (unspecialized) interface provider has empty body providing no extra members other than ones provided by the marshaller. The default package_interface_provider<T, interface_class> implementation publicly inherits members from package_const_interface_provider<T, interface_class>.
There are members of any marshaller that are mandatory and hence always present in the marshaller definition overriding and hiding members with the same name, if they are defined by the interface provider. The list of such members is given below for the description of directly customized marshallers.
Example - Complementing a marshaller interface with a methods provided by a customized interface provider
struct complex_t
{
double eReal, eImaginary;
};
template <class impl_t>
struct package_const_interface_provider<complex_t, impl_t> //an interface for a constant marshaller
{
double Re() const
{
return static_cast<const complex_t*> (static_cast<const impl_t*>(this)->data())->eReal;
}
double Im() const
{
return static_cast<const complex_t*> (static_cast<const impl_t*>(this)->data())->eImaginary;
}
//const complex_t& data() const; - would be overridden by the marshaller
//size_type size() const; - likewise, would be overridden
const complex_t& Complex() const {return *static_cast<const complex_t*> (static_cast<const impl_t*>(this)->data());}
};
template <class impl_t>
struct package_interface_provider<complex_t, impl_t> //an interface for a mutable marshaller
{
const double& Re() const
{
return static_cast<const complex_t*> (static_cast<const impl_t*>(this)->data())->eReal;
}
double& Re()
{
return static_cast<complex_t*> (static_cast<impl_t*>(this)->data())->eReal;
}
const double& Im() const
{
return static_cast<const complex_t*> (static_cast<const impl_t*>(this)->data())->eImaginary;
}
double& Im()
{
return static_cast<complex_t*> (static_cast<impl_t*>(this)->data())->eImaginary;
}
//const complex_t& data() const; - would be overridden by the marshaller
//size_type size() const; - likewise, would be overridden
const complex_t& Complex() const {return *static_cast<const complex_t*> (static_cast<const impl_t*>(this)->data());}
complex_t& Complex() {return *static_cast<complex_t*> (static_cast<impl_t*>(this)->data());}
};
Direct customization of a marshaller
The direct customization is specialization of referencing marshallers (see above) for a given type T. This practice is not recommended due to its complexity and probability of arduously detected errors such as incompatibility with other packing services. However, given all the constraints are respected, this can provide more flexibility in defining custom marshallers.
Required members
Types
1) value_type is a type to which an unpacked object is implicitly converted to during packing procedure, typically T.
2) const_reference is a type of a constant reference to an unpacked object.
3) reference is a type of a reference to an unpacked object. The reference must be constant for package_const_reference<T> and mutable for package_reference<T>.
4) get_type is a type of data to be returned to a user of a marshaller when package is being read by get method. If the type is a type of a reference or a pointer, the returned data is considered mutable for package_reference<T> and constant for package_const_reference<T>.
5) const_get_type is a type of data to be returned to a user of a marshaller when package is being read by the get method. The returned data is considered constant.
Required static methods
1) template <class U> static size_type calculate_size(U&& ref);
calculates a size of a package that would be got if an object referenced by ref was implicitly converted to value_type and the result of the conversion was packed. The parameter can be ignored by the function implementation, if the size is not based on some input data, but the parameter must still be present in the prototype. Also, calculate_size can be a method rather than a template of the method.
2) static bool verify_package_size(_In_bytecount_(cbBuf) const void* pBuf, size_type cbBuf, _Out_opt_ size_type* pSize = nullptr);
checks if the specified buffer pBuf of byte size cbBuf sufficiently described a package produced by the marshaller. A result of the check is returned as a boolean flag. Also, if the function succeeds and the pSize pointer is not null, the function writes an actual size of the package within the buffer to a buffer pointed to by the pSize parameter.
Required nonstatic methods
const void* data() const;
Returns a constant pointer to a buffer managed by the marshaller.const void* endptr() const;
Returns an address of the first byte beyond the package buffer, i.e. the byte data() + size().size_type size() const;
Returns a byte size of a package managed by the marshaller.template <class...get_params_t> const_get_type get(get_params_t&&...get_params) const;
A function or a function template with any number of parameters based on which the function returns an object from the package (i.e. a complete object, or its part).
In addition to the methods above, the following methods must be provided by the package_reference<T> marshaller.void* data();
Returns a mutable pointer to a buffer managed by the marshaller.void* endptr();
Returns a mutable pointer to first byte beyond the package buffer, i.e. the byte data() + size().template <class...get_params_t> get_type get(get_params_t&&...get_params);
A function or function template with any number of parameters based on which the function returns an object from a package (i.e. a complete object, or its part).
Constructors
1) A default constructor, i.e. package_const_reference(); and package_reference();. The constructor must nullify an internally hold a pointer to a managed buffer.
2) A copy constructor, either implicitly generated or explicitly given, i.e.
package_const_reference(const package_const_reference&);
package_reference(const package_reference&);
3) A constructor, generating a package of an object referenced by ref implicitly converted to value_type:
template <class U> package_const_reference(_Out_bytecap_(cbPackedData) void* pPackedData, size_type cbPackedData, _In_ U&& ref);
template <class U> package_reference(_Out_bytecap_(cbPackedData) void* pPackedData, size_type cbPackedData, _In_ U&& ref);
Additionally required constructors of the package_const_reference<T> marshaller.
1) package_const_reference(_In_bytecount_(cbPackedData) const void* pPackedData, size_type cbPackedData);
owns a specified buffer with a package.
2) package_const_reference(const package_reference<T>& ref);
a converting constructor from a mutable referencing marshaller.
Additionally required constructor of the package_reference<T> marshaller.
1)
package_reference(_In_bytecount_(cbPackedData) void* pPackedData, size_type cbPackedData);
owns a specified buffer with a package.
Any customization of the referencing marshallers will be automatically reflected to allocating marshallers, so there is no need to redefine them as well.
Utility components
Sequence marshallers
template <class InputIter_t>
struct sequence_to_pack_t:private std::pair<InputIter_t, InputIter_t>
{
typedef InputIter_t iterator;
typedef typename std::iterator_traits<iterator>::value_type value_type;
sequence_to_pack_t() = default;
sequence_to_pack_t(iterator itBegin, iterator itEnd):std::pair<iterator, iterator>(itBegin, itEnd);
iterator begin();
iterator begin() const;
iterator end();
iterator end() const;
};
template <class T>
struct sequence_of {};
template <class T, class interface_class>
struct package_const_interface_provider<sequence_of<T>, interface_class>
{
public:
typedef typename packing_service_provider<sequence_of<T>>::size_type size_type;
typedef packed_sequence_const_iterator<T> iterator, const_iterator;
size_type sequence_size() const;
bool empty() const;
const_iterator begin() const;
const_iterator cbegin() const;
const_iterator end() const;
const_iterator cend() const;
const void* sequence_start_address() const;
const void* sequence_end_address() const;
//deprecated:
package_const_reference<T> first_packed_item() const;
package_const_reference<T> next_packed_item(package_const_reference<T> prev) const;
};
template <class T, class interface_class>
struct package_interface_provider<sequence_of<T>, interface_class>:package_const_interface_provider<sequence_of<T>, interface_class>
{
public:
typedef packed_sequence_iterator<T> iterator;
typedef packed_sequence_const_iterator<T> const_iterator;
const_iterator begin() const;
iterator begin();
const_iterator end() const;
iterator end();
const void* sequence_start_address() const;
void* sequence_start_address();
const void* sequence_end_address() const;
void* sequence_end_address();
//deprecated:
package_const_reference<T> first_packed_item() const;
package_reference<T> first_packed_item();
package_const_reference<T> next_packed_item(package_const_reference<T> prev) const;
package_reference<T> next_packed_item(package_reference<T> prev);
};
The set of packing mechanisms includes marshallers to pack sequences of objects of the same type T. Such marshallers are specializations of corresponding referencing and allocating marshallers listed above for the type sequence_of<T> used to mark a need to pack a set of objects. A sequence to be packed is given by a pair of input iterators and is described by an object of type sequence_to_pack_t<iterator>, where iterator is a type of two input iterators specifying an inclusive beginning and and exclusive end of the range of objects to pack.
For any given T and It, such that a package for sequence_of<T> type is specified by a sequence sequence_to_pack_t<It>, the type std::iterator_traits<It>::value_type
must be implicitly convertible to T.
Using these three classes the packing can be achieved as follows:
std::vector<int> v = {1, 2, 3, 4};
auto pckg = package<sequence_of<int>>(sequence_to_pack_t<std::vector<int>::const_iterator>(v.begin(), v.end()));
In order to improve usability the packing services provide a set of sequence_to_pack overloaded function templates which deduce a specific sequence_to_pack_t instantiation being returned by them.
template <class _Cont>
sequence_to_pack_t<typename std::remove_reference<_Cont>::type::const_iterator> sequence_to_pack(const _Cont& cont);
template <class T>
sequence_to_pack_t<typename std::initializer_list<T>::const_iterator> sequence_to_pack(const std::initializer_list<T>& list);
template <class c_dynarray_elem_t>
sequence_to_pack_t<c_dynarray_elem_t*> sequence_to_pack(_In_count_(cArray) c_dynarray_elem_t* pArray, size_type cArray);
template <class c_array_elem_t, size_type N>
sequence_to_pack_t<c_array_elem_t*> sequence_to_pack(c_array_elem_t (&arr)[N]);
sequence_to_pack_t<const char*> sequence_to_pack(_In_z_ const char* psz);
sequence_to_pack_t<char*> sequence_to_pack(_In_z_ char* psz);
sequence_to_pack_t<const wchar_t*> sequence_to_pack(_In_z_ const wchar_t* psz);
sequence_to_pack_t<wchar_t*> sequence_to_pack(_In_z_ wchar_t* psz);
template <class InputIterator>
sequence_to_pack_t<InputIterator> sequence_to_pack(InputIterator itBegin, InputIterator itEnd);
Iterators
There are non-extracting and extracting iterators. The former, when dereferenced, return a package of an individual object of a packed sequence. The latter unpack the dereferenced objects on the fly.
Non-extracting iterators
These iterators are returned by the begin and end methods of the packagers of sequences.
template <class T>
class packed_sequence_const_iterator
{
public:
typedef std::forward_iterator_tag iterator_category;
typedef package_const_reference<T> value_type, *pointer, &reference;
typedef const package_const_reference<T> *const_pointer, &const_reference;
typedef typename package_const_reference<sequence_of<T>>::size_type size_type;
typedef std::ptrdiff_t difference_type;
packed_sequence_const_iterator() = default;
packed_sequence_const_iterator(const package_const_reference<sequence_of<T>>* pHost, const void* pData);
packed_sequence_const_iterator(const packed_sequence_const_iterator& right) = default;
packed_sequence_const_iterator& operator=(const packed_sequence_const_iterator& right) = default;
const_reference operator*() const;
packed_sequence_const_iterator& operator++();
bool operator==(const packed_sequence_const_iterator& right) const;
bool operator!=(const packed_sequence_const_iterator& right) const;
const_pointer operator->() const;
packed_sequence_const_iterator operator++(int);
};
template <class T>
class packed_sequence_iterator
{
public:
typedef std::forward_iterator_tag iterator_category;
typedef package_reference<T> value_type, &reference, *pointer;
typedef const package_reference<T> &const_reference, *const_pointer;
typedef typename package_reference<sequence_of<T>>::size_type size_type;
typedef std::ptrdiff_t difference_type;
packed_sequence_iterator() = default;
packed_sequence_iterator(package_reference<sequence_of<T>>* pHost, void* pData);
packed_sequence_iterator(const packed_sequence_iterator& right) = default;
packed_sequence_iterator& operator=(const packed_sequence_iterator& right) = default;
const_reference operator*() const;
reference operator*();
packed_sequence_iterator& operator++();
bool operator==(const packed_sequence_iterator& right) const;
bool operator!=(const packed_sequence_iterator& right) const;
const_pointer operator->() const;
pointer operator->();
packed_sequence_iterator operator++(int);
operator packed_sequence_const_iterator<T>() const;
};
Extracting iterators
These iterators are constructed from the non-extracting counterparts via converting constructors or the helper functions shown below.
template <class T, bool is_get_type_reference = std::is_lvalue_reference<typename package_reference<T>::get_type>::value>
class packed_sequence_extracting_const_iterator
{
public:
typedef std::forward_iterator_tag iterator_category;
typedef typename package_const_reference<T>::value_type value_type;
typedef typename package_const_reference<T>::get_type reference, const_reference;
typedef typename package_const_reference<T>::pointer pointer;
typedef typename package_const_reference<T>::const_pointer const_pointer;
typedef CAMaaS::size_type size_type;
typedef std::ptrdiff_t difference_type;
packed_sequence_extracting_const_iterator() = default;
packed_sequence_extracting_const_iterator(const packed_sequence_const_iterator<T>& it);
packed_sequence_extracting_const_iterator& operator++();
const_reference operator*() const;
const_pointer operator->() const;
packed_sequence_extracting_const_iterator operator++(int);
bool operator==(const packed_sequence_extracting_const_iterator& right) const;
bool operator!=(const packed_sequence_extracting_const_iterator& right) const;
};
template <class T, bool is_get_type_reference = std::is_lvalue_reference<typename package_reference<T>::get_type>::value>
class packed_sequence_extracting_iterator
{
public:
typedef std::forward_iterator_tag iterator_category;
typedef typename package_reference<T>::value_type value_type;
typedef typename package_reference<T>::get_type reference;
typedef typename package_reference<T>::const_get_type const_reference;
typedef typename package_reference<T>::pointer pointer;
typedef typename package_reference<T>::const_pointer const_pointer;
typedef CAMaaS::size_type size_type;
typedef std::ptrdiff_t difference_type;
packed_sequence_extracting_iterator() = default;
packed_sequence_extracting_iterator(const packed_sequence_iterator<T>& it);
packed_sequence_extracting_iterator& operator++();
reference operator*();
const_reference operator*() const;
pointer operator->();
const_pointer operator->() const;
packed_sequence_extracting_iterator operator++(int);
bool operator==(const packed_sequence_extracting_iterator& right) const;
bool operator!=(const packed_sequence_extracting_iterator& right) const;
operator packed_sequence_extracting_const_iterator<T> () const;
};
template <class T>
class packed_sequence_extracting_const_iterator<T, false>
{
public:
typedef std::forward_iterator_tag iterator_category;
typedef T value_type;
typedef const T& reference, &const_reference;
typedef const T* pointer, *const_pointer;
typedef CAMaaS::size_type size_type;
typedef std::ptrdiff_t difference_type;
packed_sequence_extracting_const_iterator() = default;
packed_sequence_extracting_const_iterator(const packed_sequence_extracting_const_iterator& r);
packed_sequence_extracting_const_iterator& operator=(const packed_sequence_extracting_const_iterator& r);
packed_sequence_extracting_const_iterator(const packed_sequence_const_iterator<T>& it);
packed_sequence_extracting_const_iterator& operator++();
const_reference operator*() const;
const_pointer operator->() const;
packed_sequence_extracting_const_iterator operator++(int);
bool operator==(const packed_sequence_extracting_const_iterator& right) const;
bool operator!=(const packed_sequence_extracting_const_iterator& right) const;
};
template <class T>
class packed_sequence_extracting_iterator<T, false>:public packed_sequence_extracting_const_iterator<T>
{
typedef std::forward_iterator_tag iterator_category;
typedef typename packed_sequence_extracting_const_iterator<T>::value_type value_type;
typedef typename packed_sequence_extracting_const_iterator<T>::reference reference;
typedef typename packed_sequence_extracting_const_iterator<T>::const_reference const_reference;
typedef typename packed_sequence_extracting_const_iterator<T>::pointer pointer;
typedef typename packed_sequence_extracting_const_iterator<T>::const_pointer const_pointer;
typedef typename packed_sequence_extracting_const_iterator<T>::size_type size_type;
typedef typename packed_sequence_extracting_const_iterator<T>::difference_type difference_type;
packed_sequence_extracting_iterator() = default;
packed_sequence_extracting_iterator(const packed_sequence_iterator<T>& it);
};
Helper functions
template <class T>
packed_sequence_extracting_const_iterator<T> make_packed_sequence_extracting_iterator(packed_sequence_const_iterator<T> it);
template <class T>
packed_sequence_extracting_iterator<T> make_packed_sequence_extracting_iterator(packed_sequence_iterator<T> it);
Tuple marshallers
Specializations for std::tuple<...> and std::pair<...> used in conjunction with the sequence marshallers to serialize multidimensional sequences. They delegate calls to the generic package implementations for the tuple elements.
template <class...Ts>
struct packing_service_provider<std::tuple<Ts...>>
{
typedef std::tuple<Ts...> value_type, get_type, const_get_type, &reference, *pointer;
typedef const std::tuple<Ts...> &const_reference, *const_pointer;
typedef CAMaaS::size_type size_type;
private:
template <class tuple_t, std::size_t..._Ind>
static size_type calculate_size(_Implementation::tuple_indices<_Ind...>, tuple_t&& tuple)
{
return package_reference<Ts...>::calculate_size(std::get<_Ind>(std::forward<tuple_t>(tuple))...);
}
template <class tuple_t, std::size_t..._Ind>
static size_type write(_Implementation::tuple_indices<_Ind...>, _Out_bytecap_(cbPackedData) void* pPackedData, size_type cbPackedData, _In_ tuple_t&& tuple)
{
return package_reference<Ts...>(pPackedData, cbPackedData, std::get<_Ind>(std::forward<tuple_t>(tuple))...).size();
}
template <std::size_t..._Ind>
static const_get_type read(_Implementation::tuple_indices<_Ind...>, _In_bytecount_(cbPackedData) const void* pPackedData, size_type cbPackedData)
{
return const_get_type(package_const_reference<Ts...>(pPackedData, cbPackedData).template get<_Ind>()...);
}
public:
template <class tuple_t>
static size_type calculate_size(tuple_t&& tuple)
{
return calculate_size(typename _Implementation::make_tuple_indices<sizeof...(Ts)>::type(), std::forward<tuple_t>(tuple));
}
template <class tuple_t>
inline static size_type write(_Out_bytecap_(cbPackedData) void* pPackedData, size_type cbPackedData, tuple_t&& tuple)
{
return write(typename _Implementation::make_tuple_indices<sizeof...(Ts)>::type(), pPackedData, cbPackedData, std::forward<tuple_t>(tuple));
}
static size_type size(_In_bytecount_(cbPackedData) const void* pPackedData, size_type cbPackedData)
{
return package_const_reference<Ts...>(pPackedData, cbPackedData).size();
}
static const_get_type read(_In_bytecount_(cbPackedData) const void* pPackedData, size_type cbPackedData)
{
return read(typename _Implementation::make_tuple_indices<sizeof...(Ts)>::type(), pPackedData, cbPackedData);
}
};
template <class T1, class T2>
struct packing_service_provider<std::pair<T1, T2>>:packing_service_provider<std::tuple<T1, T2>>
{
typedef std::pair<T1, T2> value_type, get_type, const_get_type, &reference, *pointer;
typedef const std::pair<T1, T2> &const_reference, *const_pointer;
typedef CAMaaS::size_type size_type;
static const_get_type read(_In_bytecount_(cbPackedData) const void* pPackedData, size_type cbPackedData)
{
return const_get_type(package_const_reference<T1, T2>(pPackedData, cbPackedData).template get<0>(),
package_const_reference<T1, T2>(pPackedData, cbPackedData).template get<1>());
}
};
Reference marshallers
A specialization for std::reference_wrapper<T> used to store marshal references to objects. The specialization for generic T produces a pointer to the packed object of type T.
template <class T>
struct packing_service_provider<std::reference_wrapper<T>>
{
typedef typename std::decay<T>::type value_type;
typedef typename std::add_lvalue_reference<T>::type reference;
typedef std::reference_wrapper<T> get_type;
typedef std::reference_wrapper<T> const_get_type;
typedef typename std::add_pointer<typename std::remove_reference<T>::type>::type pointer;
typedef typename std::add_lvalue_reference<typename std::add_const<typename std::remove_reference<T>::type>::type>::type const_reference;
typedef typename std::add_pointer<typename std::add_const<typename std::remove_reference<T>::type>::type>::type const_pointer;
typedef CAMaaS::size_type size_type;
static size_type calculate_size(std::reference_wrapper<T>)
{
return package_reference<const_pointer>::calculate_size(nullptr);
}
static _Success_(return != false) bool verify_package_size(_In_bytecount_(cbBuf) const void* pBuf, size_type cbBuf, _Out_opt_ size_type* pSize = nullptr)
{
return package_reference<const_pointer>::verify_package_size(pBuf, cbBuf, pSize);
}
static size_type write(_Out_bytecap_(cbPackedData) void* pPackedData, size_type cbPackedData, _In_ std::reference_wrapper<T> refObject)
{
return package_reference<const_pointer>(pPackedData, cbPackedData, std::addressof(refObject.get())).size();
}
static size_type size(_In_bytecount_(cbPackedData) const void* pPackedData, size_type cbPackedData)
{
return package_const_reference<const_pointer>(pPackedData, cbPackedData).size();
}
static const_get_type read(_In_bytecount_(cbPackedData) const void* pPackedData, size_type cbPackedData)
{
auto pckg = package_const_reference<pointer>(pPackedData, cbPackedData);
return *pckg.get();
}
static get_type read(_In_bytecount_(cbPackedData) void* pPackedData, size_type cbPackedData)
{
auto pckg = package_reference<pointer>(pPackedData, cbPackedData);
return *pckg.get();
}
};
make_package and make_const_package
The following two functions deduce package type from a specified parameter list.
template <class...packed_input_t>
inline package<default_package_allocator, typename input_type_to_package_value_type_t<packed_input_t>::type...> make_package(packed_input_t&&...packed_params);
template <class...packed_input_t>
inline const_package<default_package_allocator, typename input_type_to_package_value_type_t<packed_input_t>::type...> make_const_package(packed_input_t&&...packed_params);
The first overload constructs a package<...> object specialized for the arguments and using a default constructed allocator of the default_package_allocator type.
The first overload constructs a const_package<...> object specialized for the arguments and using a default constructed allocator of the default_package_allocator type.
With these introduced the above example can be overwritten as follows:
auto pckg = make_package({1, 2, 3, 4});
Also, an implemetation of the sequence marshallers, is chosen based on two factors.
1) whether a range to be packed is described by random-access iterators, or by more restrictive iterators. This optimization is always performed and does not require additional efforts from a user or one who customizes marshallers of each element of the sequence.
2) whether a packed data element can be randomly accessed by a user at a constant time. By default, this is considered true only if a type being packed is scalar, i.e. for which std::is_scalar<T>::value is true. To improve performance of a marshaller specialized for a custom type T, given the latter has a constant size in packed form, an implementer of the type T has to define either a known nested type random_composition_access_tag which should resolve to std::true_type, or externally (relatively to the class T) specialize the contiguous_elements_have_constant_access structure template for that type providing an interface of std::true_type (for instance, by inheritance).
Usage examples
Any compound object should packed using the patterns below.
Example 1. Consider a function process receiving a buffer of marshalled data pBuf of size cbBuf known to be a package of an int value, some object of class C, a double value, and a string.
struct C
{
int some_C_method();
};
void process(const void* pBuf, size_type cbBuf)
{
auto compound_package = package_const_reference<int, C, double, sequence_of(char)> (pBuf, cbBuf);//See also sequence_of class template and specializations
//of service and interface providers for sequence_of<T>.
process_int(compound_package.template get<0>()); //passes the first element of the package to another function
process_double(compound_package.template get<2>()); //passes the third element of the package to another function
compound_package.template get<1>().some_C_method(); //calls a method upon an unpacked object of type C, assuming that package_const_reference<C>::get();
//returns the object, which is true for a defaullt marshaller.
process_string(std::string(static_cast<const char*> (compound_package.get_packed_item<3>().sequence_start_address()),
static_cast<const char*>(compound_package.get_packed_item<3>().sequence_end_address()))); //see sequence_of<T>.
}
Example 2. Creating allocating marshaller using deduction and packing compound data.
void send_personal_data(const wchar_t* pszName, int age)
{
auto pckg = make_package(sequence_to_pack(pszName), age);
send(pckg.data(), pckg.size());
}
Example 3. Customizing a marshaller for a polymorphic class and packing a set of such polymorphyc objects.
struct IPerson
{
virtual const wchar_t* name() const = 0;
virtual unsigned age() const = 0;
virtual double weight() const = 0;
virtual ~IPerson() {}
};
class CPerson:public IPerson
{
int m_age;
std::wstring m_strName;
double m_weight;
public:
template <class name_t>
CPerson(name_t&& name, unsigned age, double weight):m_strName(std::forward<name_t>(name)), m_age(age), m_weight(weight) {}
virtual const wchar_t* name() const {return m_strName.c_str();}
virtual unsigned age() const {return m_age;}
virtual double weight() const {return m_weight;}
};
template<>
struct packing_service_provider<std::unique_ptr<IPerson>>:default_packing_service_provider<std::unique_ptr<IPerson>> //redefining a service provider
{
typedef package_const_reference<int, sequence_of<char>, double> const_packer_t;
typedef package_reference<int, sequence_of<char>, double> packer_t;
typedef std::unique_ptr<IPerson> value_type;
typedef std::unique_ptr<IPerson> get_type;
typedef std::unique_ptr<const IPerson> const_get_type;
typedef std::uint32_t size_type;
static size_type calculate_size(const std::unique_ptr<IPerson>& ref)
{
std::string str_to_pack = std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(std::wstring(ref->name()));
return package_const_reference<int, sequence_of<char>, double>::calculate_size(std::ignore, sequence_to_pack_t<typename std::string::const_iterator>(str_to_pack.begin(), str_to_pack.end()), std::ignore);
}
static bool verify_package_size(_In_bytecount_(cbBuf) const void* pBuf, size_type cbBuf, _Out_opt_ size_type* pSize = nullptr)
{
return package_const_reference<int, sequence_of<char>, double>::verify_package_size(pBuf, cbBuf, pSize);
}
static size_type write(_Out_bytecap_(cbPackedData) void* pPackedData, size_type cbPackedData, const std::unique_ptr<IPerson>& person)
{
std::string str_to_pack = std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(std::wstring(person->name()));
package_const_reference<int, sequence_of<char>, double> packed(pPackedData, cbPackedData, person->age(), sequence_to_pack(str_to_pack), person->weight());
return packed.size();
}
static get_type read(_In_bytecount_(cbBuf) const void* pBuf, size_type cbBuf)
{
auto packed = package_const_reference<int, sequence_of<char>, double> (pBuf, cbBuf);
std::string packed_string(static_cast<const char*>(packed.get_packed_item<1>().sequence_start_address()), static_cast<const char*>(packed.get_packed_item<1>().sequence_end_address()));
return std::unique_ptr<IPerson> (new CPerson(std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(packed_string), packed.template get<0>(), packed.template get<2>()));
}
};
int main(int, char**)
{
std::unique_ptr<IPerson> set_of_people[] =
{
std::unique_ptr<IPerson>(new CPerson(L"Andrey", 1, 2.2)),
std::unique_ptr<IPerson>(new CPerson(L"Aleksey", 1, 2.2)),
std::unique_ptr<IPerson>(new CPerson(L"Sergey", 1, 2.2))
};
auto pckg = make_package(sequence_to_pack(set_of_people));
send(pckg.data(), pckg.size());
return 0;
};