Boost Serialization: Serialize/Export derived class without default constructor - c++11

I need to serialize all the options on my CommunicationLayer, which is basically a wrapper around serial port, which I will use in an initialization file of sorts.
This class doesn't have a default constructor, but implements a pure virtual class. When I first ran this I got the exception that I didn't register/export the derived class. So I did that. But when I added the export I got the exception that it couldn't find a save/load function:
no matching function for call to ‘save(boost_1_69_0::archive::xml_oarchive&, const amp::communication::RS485CommunicationLayer&, const boost_1_69_0::serialization::version_type&)’
save(ar, t, v);
This is true! But I can't use the save/load and use save_construct_data and load_construct_data, because the absence of a default constructor. How do I tell boost/serde to use these instead?? Is it the SPLIT_FREE macro?
I'm trying to work myself through the documentation, but it's really hard to find everything.
Following are the base and derived classes, I tried to shorten the derived class as much as possible.
//BASE
class ICommunicationLayer {
friend class boost::serialization::access;
template<class Archive>
inline void serialize(Archive & ar, const unsigned int file_version) {};
public:
virtual ~ICommunicationLayer();
virtual std::size_t write(const char* const buffer, std::size_t buffSize) = 0;
virtual std::size_t readUntil(std::vector<char>& buffer, char delim, std::chrono::microseconds timeout) = 0;
};
// DERIVED
#include "ICommunicationLayer.h"
#include <boost/asio.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/nvp.hpp>
namespace amp {
namespace communication {
class RS485CommunicationLayer final : public ICommunicationLayer {
public:
RS485CommunicationLayer(
const std::string& path,
unsigned int baud_rate,
// other options
);
~RS485CommunicationLayer();
std::size_t write(const char* const buffer, const size_t size) override;
std::size_t readUntil(std::vector<char>& buffer, char delim,std::chrono::microseconds timeout) override;
std::string getPath() const;
private:
friend class boost::serialization::access;
std::string path;
// rest of impl
};
} /* namespace communication */
} /* namespace amp */
BOOST_SERIALIZATION_SPLIT_FREE(amp::communication::RS485CommunicationLayer)
BOOST_CLASS_EXPORT(amp::communication::RS485CommunicationLayer)
namespace boost_1_69_0 {
namespace serialization {
template<class Archive>
inline void save_construct_data(Archive& ar, const amp::communication::RS485CommunicationLayer* comLayer, const unsigned int version) {
ar << boost::serialization::make_nvp(
BOOST_PP_STRINGIZE(amp::communication::ICommunicationLayer),
boost::serialization::base_object<amp::communication::ICommunicationLayer>(*comLayer)
);
ar << boost::serialization::make_nvp("path", const_cast<std::string>(comLayer->getPath());
// other options
}
template<class Archive>
inline void load_construct_data(Archive& ar, amp::communication::RS485CommunicationLayer* comLayer, const unsigned int version) {
ar >> boost::serialization::base_object<amp::communication::ICommunicationLayer>(*comLayer);
std::string path;
ar >> path;
// other options
new(comLayer) amp::communication::RS485CommunicationLayer(path, baudrate, /* other options */);
}
}
}

The construct data is an addition to regular serialization. You still need to serialize. As a matter of fact, serializing base_object needs to be in the constructed-object serialize implementation.
Also keep in mind that you need to EXPORT after including the relevant archive type(s).
You have a number of spots with redundant top-level const (even a const-cast). Top-level const on return type/arguments isn't part of the function signature.
Finally, I don't know what namespace boost_1_69_0 is, but that should not work. Use namespace boost. If you're playing with macros to define it like that, I'd suggest to stop doing that. Instead consider putting the overloads in the ADL-associated namespace for your types!
Here's the whole thing made self-contained and working:
Live On Coliru
#include <boost/serialization/access.hpp>
#include <boost/serialization/assume_abstract.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <chrono>
// BASE
namespace amp::communication {
using duration = std::chrono::steady_clock::duration;
class ICommunicationLayer {
friend class boost::serialization::access;
template <class Archive> inline void serialize(Archive&, unsigned){}
public:
virtual ~ICommunicationLayer();
virtual size_t write(char const* const buffer, size_t buffSize) = 0;
virtual size_t readUntil(std::vector<char>& buffer, char delim, duration timeout) = 0;
};
} // namespace amp::communication
// DERIVED
//#include "ICommunicationLayer.h"
#include <boost/asio.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/split_free.hpp>
namespace amp { namespace communication {
class RS485CommunicationLayer final : public ICommunicationLayer {
public:
RS485CommunicationLayer(std::string const& path, unsigned baud_rate)
: path(path)
, baud_rate(baud_rate) {}
~RS485CommunicationLayer();
size_t write(char const* const buffer, const size_t size) override;
size_t readUntil(std::vector<char>& buffer, char delim, duration timeout) override;
std::string getPath() const { return path; }
unsigned getBaudrate() const { return baud_rate; }
private:
friend class boost::serialization::access;
template <typename Ar> void serialize(Ar& ar, unsigned) {
ar& boost::make_nvp("ICommunicationLayer",
boost::serialization::base_object<ICommunicationLayer>(*this));
}
std::string path;
unsigned baud_rate;
// rest of impl
};
// ADL resolved overloads
template <class Archive>
inline void save_construct_data(Archive& ar, RS485CommunicationLayer const* p, unsigned) {
auto path = p->getPath();
unsigned baud_rate = p->getBaudrate();
ar& BOOST_NVP(path) & BOOST_NVP(baud_rate);
}
template <class Archive>
inline void load_construct_data(Archive& ar, RS485CommunicationLayer* p, unsigned) {
std::string path;
unsigned baud_rate;
ar >> BOOST_NVP(path) >> BOOST_NVP(baud_rate);
new (p) RS485CommunicationLayer(path, baud_rate);
}
}} // namespace amp::communication
BOOST_SERIALIZATION_ASSUME_ABSTRACT(amp::communication::ICommunicationLayer)
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <iostream>
#include <sstream>
BOOST_CLASS_EXPORT(amp::communication::ICommunicationLayer)
BOOST_CLASS_EXPORT(amp::communication::RS485CommunicationLayer)
int main() {
using namespace amp::communication;
using Ptr = std::shared_ptr<ICommunicationLayer>;
std::vector<Ptr> ptrs{nullptr, std::make_shared<RS485CommunicationLayer>("hello", 115'200)}, roundtrip;
ptrs.push_back(ptrs.back()); // one duplicate for testing purposes
std::stringstream xml;
{
boost::archive::xml_oarchive oa(xml);
oa << boost::make_nvp("root", ptrs);
}
std::cout << xml.str();
{
boost::archive::xml_iarchive ia(xml);
ia >> boost::make_nvp("root", roundtrip);
}
bool verify = roundtrip.size() == ptrs.size();
verify &= (roundtrip.at(0) == nullptr);
verify &= (roundtrip.at(1) == roundtrip.at(2));
std::cout << "Roundtrip verify " << (verify?"OK":"FAILED") << std::endl;
}
// to satisfy linker
namespace amp { namespace communication {
ICommunicationLayer::~ICommunicationLayer() = default;
RS485CommunicationLayer::~RS485CommunicationLayer() = default;
size_t RS485CommunicationLayer::write(char const*, size_t) { return 0; }
size_t RS485CommunicationLayer::readUntil(std::vector<char>&, char, duration) { return 0; }
}} // namespace amp::communication
Prints
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="19">
<root class_id="0" tracking_level="0" version="0">
<count>3</count>
<item_version>1</item_version>
<item class_id="1" tracking_level="0" version="1">
<px class_id="-1"></px>
</item>
<item>
<px class_id="2" class_name="amp::communication::RS485CommunicationLayer" tracking_level="1" version="0" object_id="_0">
<path>hello</path>
<baud_rate>115200</baud_rate>
<ICommunicationLayer class_id="3" tracking_level="0" version="0"></ICommunicationLayer>
</px>
</item>
<item>
<px class_id_reference="2" object_id_reference="_0"></px>
</item>
</root>
</boost_serialization>
Roundtrip verify OK
Afterthought
Perhaps the best location for load/save construct data is as hidden friends. That way you don't even need public accessors for every bit of construct-data:
Live On Coliru
class RS485CommunicationLayer final : public ICommunicationLayer {
public:
RS485CommunicationLayer(std::string const& path, unsigned baud_rate)
: path(path)
, baud_rate(baud_rate) {}
~RS485CommunicationLayer() override;
size_t write(char const* const buffer, const size_t size) override;
size_t readUntil(std::vector<char>& buffer, char delim, duration timeout) override;
private:
std::string path;
unsigned baud_rate;
// rest of impl
template <class Archive>
friend void save_construct_data(Archive& ar, RS485CommunicationLayer const* p, unsigned) {
auto path = p->path;
unsigned baud_rate = p->baud_rate;
ar& BOOST_NVP(path) & BOOST_NVP(baud_rate);
}
template <class Archive>
friend void load_construct_data(Archive& ar, RS485CommunicationLayer* p, unsigned) {
std::string path;
unsigned baud_rate;
ar >> BOOST_NVP(path) >> BOOST_NVP(baud_rate);
new (p) RS485CommunicationLayer(path, baud_rate);
}
friend class boost::serialization::access;
template <typename Ar> void serialize(Ar& ar, unsigned) {
ar& boost::make_nvp("ICommunicationLayer",
boost::serialization::base_object<ICommunicationLayer>(*this));
}
};

Related

why is specialised template being called?

I created a template called debug which is indirectly invoked through the function errorMsg. I then specialised the template to account for char * (code w/comments below hopefully helps with explanations)
After some playing around I was surprised that even though I defined the template specialisations at a point after they're called in errorMsg(), they were still being used.
I would have assumed because it had not yet been defined at the point the main template would instantiate a default copy or an error would occur
Any help resolving this issue would be great thanks
#include "header.h"
int main()
{
//std::vector<std::string> s_vec{"abc","cede","rfind"};
int i = 3;
int *j = &i;
errorMsg(std::cout,"hey"); //<---calls debug
}
//defined specialisations after its invoked inside errorMsg
template <>
inline std::string debug(char * p)
{
std::cout<<"specialsed char"<<std::endl;
return debug(std::string(p));
}
template <>
inline std::string debug(const char *p)
{
std::cout<<"specialised const char"<<std::endl;
return debug(std::string(p));
(header.h)
#include <iostream>
#include <sstream>
#include <string>
//(1)
template <typename T>
std::string debug(const T&s)
{
std::cout<<"unspecialised obj"<<std::endl;
std::ostringstream oss;
oss<<s;
return oss.str();
}
//(2)
template <typename T>
std::string debug(T *ptr)
{
std::cout<<"unspecialised raw ptr"<<std::endl;
std::ostringstream oss;
oss << "pointer: "<<ptr;
if (ptr)
{
oss<<" "<<debug(*ptr);
}
else
oss<<" null pointer";
return oss.str();
}
template <typename T, typename... Args> void print(std::ostream &os,const T &t,const Args&...rest);
template <typename T> std::ostream &print(std::ostream &os, const T &t);
template <typename... Args>
void errorMsg(std::ostream &os,Args &&...args)
{
print(os,debug(std::forward<Args>(args))...); //debug called here
}
template <typename T>
std::ostream &print(std::ostream &os, const T &t)
{
return os<<t<<std::endl;
}
template <typename T, typename... Args>
void print(std::ostream &os,const T &t,const Args&...rest)
{
os<<t<<", ";
print(os,rest...);
}
result:
specialised const char
unspecialised obj
hey
[temp.expl.spec]/6 If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.
Your program is ill-formed; no diagnostic required.

Template function taking generic pointer to member function with both const& and by-value implementations

I want to have a template function which accepts unary member-function pointers of an instance of some generic type.
My problem is that I must support both void(T val) and void(const T& val) member functions.
I have written one template function for each case and it works fine, but this leads to code duplication since the function logic is completely the same. (I found something completely similar here: Function taking both pointer to member-function and pointer to const member-function but I fail to see a definitive solution).
An example of the generic type mentioned above:
using UserAddress = std::string;
class User
{
private:
int mHeight;
UserAddress mAddress;
public:
void SetHeight(int height){mHeight = height;}
void SetAddress(const UserAddress& address){mAddress = address;}
};
Where UserAddress is some heavy type I want to pass by reference.
My templated function:
template <typename TPersistentObject>
class Persistence
{
private:
std::map<std::string, std::function<void(User*)>> mSetterOfProperty;
template <typename TPersistentObject, typename TPropertyValue>
void DefinePropertySettingMethod(const std::string& propertyName,
void (TPersistentObject::*propertySetter)(TPropertyValue), std::function<TPropertyValue(void)> dataReader)
{
mSetterOfProperty[propertyName] =
[propertySetter, columnDataReader](TPersistentObject* persistentObject)
{
(persistentObject->*propertySetter)(dataReader());
};
}
};
/// Const& implementation leading to code duplication
template <typename TPersistentObject, typename TPropertyValue>
void DefinePropertySettingMethod(const std::string& propertyName,
void (TPersistentObject::*propertySetter)(const TPropertyValue&), std::function<TPropertyValue(void)> dataReader)
{
...
}
};
Is there some way to define this function to support the following:
int main()
{
auto intDataReader = []() {
return 1;
};
auto stringDataReader = []() {
return UserAddress("Next Door");
};
Persistence p;
p.DefinePropertySettingMethod<User,int>("Height", &User::SetHeight, intDataReader);
p.DefinePropertySettingMethod<User,UserAddress>("Address", &User::SetAddress, stringDataReader);
}
Thanks to Igor Tandetnik 's tip I managed to compile a solution. std::enable_if is not what I needed though since I did not need to deactivate an overload (or at least I couldn't come to a solution using it).
std::conditional did the trick.
Here is the code:
#include <string>
#include <functional>
#include <map>
#include <string>
#include <type_traits>
using UserAddress = std::string;
class User
{
private:
int mHeight;
UserAddress mAddress;
public:
void SetHeight(int height){mHeight = height;}
void SetAddress(const UserAddress& address){mAddress = address;}
};
template <typename TPersistentObject>
class Persistence
{
public:
std::map<std::string, std::function<void(TPersistentObject*)>> mSetterOfProperty;
template <typename TPropertyValue>
void DefinePropertySettingMethod(const std::string& propertyName,
void (TPersistentObject::*propertySetter)(TPropertyValue),
std::function<
typename std::conditional<!std::is_same<TPropertyValue, typename std::decay<TPropertyValue>::type>::value,
typename std::decay<TPropertyValue>::type, TPropertyValue>::type
(void)> dataReader)
{
mSetterOfProperty[propertyName] =
[propertySetter, dataReader](TPersistentObject* persistentObject)
{
(persistentObject->*propertySetter)(dataReader());
};
}
};
int main()
{
std::function<int()> intDataReader = []() {
return 1;
};
std::function<std::string()> stringDataReader = []() {
return UserAddress("Next Door");
};
Persistence<User> p;
p.DefinePropertySettingMethod("Height", &User::SetHeight, intDataReader);
p.DefinePropertySettingMethod("Address", &User::SetAddress, stringDataReader);
}

Find boost multi index Tag to index and number of indices

I have a template class(CrMultiIndex) that receive as template parameter a definition of boost multi index(GlobalHash).
I need :
To add statistics to my template class according to Index used.
So i need a way to resize the vector(m_StatsByIndex) at init with the number of existing indices.
I still want the user to search according to tag and not index number.
So i need a way to convert from tag to index number so i can update statistics in vector according to index in vector.
I have template class
template <typename KeysType, typename MultiIndexType>
class CrMultiIndex
{
std::vector<SrStatisticsByIndex> m_StatsByIndex;
public:
MultiIndexType *m_pMultiIndex=NULL;
CrMultiIndex()
{
m_pMultiIndex = new MultiIndexType(typename
MultiIndexType::ctor_args_list());
}
Here is the definition of boost multi index container:
typedef boost::multi_index::multi_index_container<
CrUsersKeys,
UsersKey_hash_indices/*,
bip::allocator<CrUsersKeys,bip::managed_shared_memory::segment_manager>*/
> GlobalHash;
with a search function according to Tag
template <typename TagType,typename SearchingKey>
typename MultiIndexType::template index<TagType>::type::iterator
GetIteratorBy(SearchingKey & key)
{
return m_pMultiIndex->template get<TagType>().find(key) ;
}
Code is at http://coliru.stacked-crooked.com/a/d97195a6e4bb7ad4
You'd need to query the embedded index type lists:
typedef typename MultiIndexType::index_type_list::size NumberOfIndexes;
template <typename Tag> constexpr static size_t IndexOfTag() {
namespace mpl = boost::mpl;
using tl = typename MultiIndexType::index_type_list;
using B = typename mpl::begin<tl>::type;
using helper = typename MultiIndexType::template index<Tag>;
static_assert(helper::index_found, "index not found");
auto N = mpl::distance<B, typename helper::iter>::value;
return N;
}
Or, using Boost Mpl all the way:
typedef typename MultiIndexType::index_type_list::size NumberOfIndexes;
template <typename Tag> constexpr static size_t IndexOfTag() {
namespace mpl = boost::mpl;
using tl = typename MultiIndexType::index_type_list;
using B = typename mpl::begin<tl>::type;
using E = typename mpl::end<tl>::type;
using It = typename mpl::find_if<tl, bmi::detail::has_tag<Tag> >::type;
static_assert(not std::is_same<E, It>(), "index not found");
auto N = mpl::distance<B, It>::value;
return N;
}
You can use it like so:
template <typename TagType, typename SearchingKey>
typename MultiIndexType::template index<TagType>::type::iterator
GetIteratorBy(SearchingKey &key) {
auto& idx = m_pMultiIndex.template get<TagType>();
auto& stats = GetStats<TagType>();
auto it = idx.find(key);
++(it == idx.end()? stats.searchedNotFound : stats.searchedSuccessfully);
return it;
}
DEMO
Note the code has been simplified:
Live On Coliru
#include <iostream>
#include <boost/multi_index/member.hpp> // for member
#include <boost/multi_index/hashed_index.hpp> // for hashed_unique
#include <boost/multi_index/ordered_index.hpp> // for ordered_non_unique
#include <boost/multi_index_container.hpp> // for multi_index_container
namespace bmi = boost::multi_index;
struct SrStatisticsByIndex {
int deleted;
int searchedSuccessfully;
int searchedNotFound;
};
template <typename MultiIndexType, typename ValueType = typename MultiIndexType::value_type>
class CrMultiIndex {
typedef typename MultiIndexType::index_type_list::size NumberOfIndexes;
template <typename Tag> constexpr static size_t IndexOfTag() {
using tl = typename MultiIndexType::index_type_list;
using B = typename boost::mpl::begin<tl>::type;
using helper = typename MultiIndexType::template index<Tag>;
static_assert(helper::index_found, "index not found");
return boost::mpl::distance<B, typename helper::iter>::value;
}
public:
MultiIndexType m_pMultiIndex;
template <typename Tag> SrStatisticsByIndex& GetStats()
{ return m_StatsByIndex.at(IndexOfTag<Tag>()); }
template <typename Tag> SrStatisticsByIndex const& GetStats() const
{ return m_StatsByIndex.at(IndexOfTag<Tag>()); }
// All the protected function are non locking function
template <typename TagType, typename SearchingKey>
typename MultiIndexType::template index<TagType>::type::iterator
GetIteratorBy(SearchingKey &key) {
auto& idx = m_pMultiIndex.template get<TagType>();
auto& stats = GetStats<TagType>();
auto it = idx.find(key);
++(it == idx.end()? stats.searchedNotFound : stats.searchedSuccessfully);
return it;
}
void Insert(ValueType const &key) {
std::cout << (m_pMultiIndex.insert(key).second? "success":"failed") << std::endl;
}
private:
std::vector<SrStatisticsByIndex> m_StatsByIndex { NumberOfIndexes() };
};
class CrUsersValue {
int val1;
int val2;
};
class CrUsersKeys {
public:
int IMSI;
int TIMESTAMP;
CrUsersValue val;
};
typedef boost::multi_index::multi_index_container<
CrUsersKeys,
bmi::indexed_by<
bmi::ordered_non_unique<bmi::tag<struct TIMESTAMP_tag>,
bmi::member<CrUsersKeys, int, &CrUsersKeys::TIMESTAMP> >,
bmi::hashed_unique<bmi::tag<struct IMSI_tag>,
bmi::member<CrUsersKeys, int, &CrUsersKeys::IMSI> /*, boost::hash<int>, std::equal_to<int>*/>
>
/*, bip::allocator<CrUsersKeys,bip::managed_shared_memory::segment_manager>*/
>
GlobalHash;
int main() {
CrMultiIndex<GlobalHash> multi;
CrUsersKeys key;
key.IMSI = 2;
multi.Insert(key);
int searchKey = 2;
auto it = multi.GetIteratorBy<IMSI_tag>(searchKey);
if (it != multi.m_pMultiIndex.get<IMSI_tag>().end())
std::cout << "found " << std::endl;
}
Prints
success
found
As a supplement to sehe's answer, this a rewrite of IndexOfTag that does not depend on undocumented Boost.MultiIndex features:
Live On Coliru
template<typename MultiIndexContainer,std::size_t N=0>
struct index_position:index_position<MultiIndexContainer,N+1>
{
using index_type=typename boost::multi_index::nth_index<MultiIndexContainer,N>::type;
using index_position<MultiIndexContainer,N+1>::case_of;
static constexpr std::size_t case_of(std::in_place_type_t<index_type>){return N;}
};
template<typename MultiIndexContainer>
struct index_position<
MultiIndexContainer,
boost::mpl::size<typename MultiIndexContainer::index_type_list>::value
>
{
static constexpr void case_of(...){}
};
template <typename MultiIndexContainer,typename Tag>
constexpr std::size_t IndexOfTag()
{
using index_type=typename boost::multi_index::index<MultiIndexContainer,Tag>::type;
return index_position<MultiIndexContainer>::case_of(std::in_place_type<index_type>);
}
Edit: In C++14:
Live On Coliru
template<typename MultiIndexContainer,std::size_t N=0>
struct index_position:index_position<MultiIndexContainer,N+1>
{
using index_type=typename boost::multi_index::nth_index<MultiIndexContainer,N>::type;
using index_position<MultiIndexContainer,N+1>::case_of;
static constexpr std::size_t case_of(index_type*){return N;}
};
template<typename MultiIndexContainer>
struct index_position<
MultiIndexContainer,
boost::mpl::size<typename MultiIndexContainer::index_type_list>::value
>
{
static constexpr void case_of(...){}
};
template <typename MultiIndexContainer,typename Tag>
constexpr std::size_t IndexOfTag()
{
using index_type=typename boost::multi_index::index<MultiIndexContainer,Tag>::type;
return index_position<MultiIndexContainer>::case_of((index_type*)(nullptr));
}

VStudio 2012 Create custom allocator for container of move-only type

I am trying to create an stl container of a move-only type that uses its own allocator in VStudio 2012.
The trouble is: it seems as though I have to provide a construct function for the allocator which in turn needs access to a public copy constructor on the contained type.
I either get:
error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'
or
error C2039: 'construct' : is not a member of 'MyAllocator'
The same code works in clang so I suspect the problem is due to Microsoft but can anyone suggest a possible work around?
This is my code for minimal reproduction
#include <memory>
#include <vector>
using namespace std;
template< typename T>
struct MyAllocator
{
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef const value_type* const_pointer;
typedef const value_type& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
template<class t_other>
struct rebind
{
typedef MyAllocator<t_other> other;
};
MyAllocator():m_id(0) {}
MyAllocator(int id):m_id(id){}
template <class T>
MyAllocator(const MyAllocator<T>& other)
:m_id(other.getId())
{
}
T* allocate(std::size_t n)
{
return reinterpret_cast<T*>(malloc(sizeof(T) * n));
}
void deallocate(T* p, std::size_t n)
{
free(p);
}
int getId() const{ return m_id;}
//Have to add these although should not be necessary
void construct(pointer mem, const_reference value)
{
std::_Construct(mem, value);
}
void destroy(pointer mem)
{
std::_Destroy(mem);
}
private:
int m_id;
};
template <class T1, class U>
bool operator==(const MyAllocator<T1>& lhs, const MyAllocator<U>& rhs)
{
return lhs.getId() == rhs.getId() ;
}
template <class T1, class U>
bool operator!=(const MyAllocator<T1>&, const MyAllocator<U>&)
{
return lhs.getId() != rhs.getId();
}
//define a move only type
typedef unique_ptr<uint32_t> MyIntPtr;
//define a container based on MyIntPtr and MyAllocator
typedef vector<MyIntPtr, MyAllocator<MyIntPtr> > MyVector;
int main(int argc, char* argv[])
{
MyAllocator<MyIntPtr> alloc1(1);
MyVector vec(alloc1);
uint32_t* rawPtr = new uint32_t;
*rawPtr = 18;
vec.emplace_back(rawPtr);
return 0;
}
The error you get is because you try to construct a std::unique_ptr from a constant reference to a std::unique_ptr of the same type - and there is no such constructor.
You can rework your construct method to take an an rvalue reference and then everything compiles nicely:
void construct(pointer mem, value_type&& value)
{
std::_Construct(mem, std::move(value));
}

How to use BGL’s adjacency_list with standard unorunordered_set as the out edges list template parameter

I’m trying to use boost’s graph library adjacency_list with C++11 unorunordered_set.
While using it as the second template argument (i.e. the vertex list) compiles fine, when trying to use it as the “out edges” types (the first template parameter) I get the following error:
“"The C++ Standard doesn't provide a hash for this type."
Here is the code at question:
#include <unordered_set>
#include <boost/config.hpp>
#include <boost/graph/adjacency_list.hpp>
struct NodeProperty{
std::string FirstName;
std::string LastName;
};
namespace std{
template<>
struct hash<NodeProperty>
{
std::hash<std::string> hasher;
size_t operator()(const NodeProperty& key) const
{
return hasher(key.FirstName) ^ hasher(key.LastName);
}
};
}
namespace boost {
struct std_unorunordered_setS{};
template <class ValueType>
struct container_gen<std_unorunordered_setS, ValueType> {
typedef std::unordered_set<ValueType> type;
};
template <>
struct parallel_edge_traits<std_unorunordered_setS> {
typedef disallow_parallel_edge_tag type;
};
}
using namespace boost;
int main(int, char*[])
{
typedef adjacency_list< std_unorunordered_setS, std_unorunordered_setS, bidirectionalS,
NodeProperty > Graph;
typedef graph_traits<Graph>::vertex_descriptor Vertex;
}
Many 10x,
Andrey
The out-edge list does use a implementation defined wrapper types.
If you read the message well, you can see you need the hash of something of a type-erased iterator wrapper. You need these specializations (stolen from detail/adjacency_list.hpp):
namespace std {
template <typename V> struct hash<boost::detail::stored_edge<V> > {
std::size_t operator()(const boost::detail::stored_edge<V> &e) const { return hash<V>()(e.m_target); }
};
template <typename V, typename P> struct hash<boost::detail::stored_edge_property<V, P> > {
std::size_t operator()(const boost::detail::stored_edge_property<V, P> &e) const { return hash<V>()(e.m_target); }
};
template <typename V, typename I, typename P> struct hash<boost::detail::stored_edge_iter<V, I, P> > {
std::size_t operator()(const boost::detail::stored_edge_iter<V, I, P> &e) const { return hash<V>()(e.m_target); }
};
}
CAVEAT: I'd just use the hash_setS selector that is built in (which uses boost::unordered_set). That way you won't depend on implementation details that are expressly not in the public headers.
Both flavours Live On Coliru
#include <unordered_set>
#include <boost/config.hpp>
#include <boost/graph/adjacency_list.hpp>
struct NodeProperty {
std::string FirstName;
std::string LastName;
};
namespace std {
template <> struct hash<NodeProperty> {
std::hash<std::string> hasher;
size_t operator()(const NodeProperty &key) const { return hasher(key.FirstName) ^ hasher(key.LastName); }
};
}
namespace std {
template <typename V> struct hash<boost::detail::stored_edge<V> > {
std::size_t operator()(const boost::detail::stored_edge<V> &e) const { return hash<V>()(e.m_target); }
};
template <typename V, typename P> struct hash<boost::detail::stored_edge_property<V, P> > {
std::size_t operator()(const boost::detail::stored_edge_property<V, P> &e) const { return hash<V>()(e.m_target); }
};
template <typename V, typename I, typename P> struct hash<boost::detail::stored_edge_iter<V, I, P> > {
std::size_t operator()(const boost::detail::stored_edge_iter<V, I, P> &e) const { return hash<V>()(e.m_target); }
};
}
namespace boost {
struct std_unordered_setS {};
template <class ValueType> struct container_gen<std_unordered_setS, ValueType> {
typedef std::unordered_set<ValueType> type;
};
template <> struct parallel_edge_traits<std_unordered_setS> { typedef disallow_parallel_edge_tag type; };
}
using namespace boost;
int main() {
#ifdef USE_STD
typedef adjacency_list<std_unordered_setS, std_unordered_setS, bidirectionalS, NodeProperty> Graph;
#else
typedef adjacency_list<hash_setS, hash_setS, bidirectionalS, NodeProperty> Graph;
#endif
// typedef graph_traits<Graph>::vertex_descriptor Vertex;
}

Resources