serialize is not a member of std::unique_ptr - c++

Is this question more appropriate for Boost forums? The complete code is referenced below and I do not consider that I have incorrectly attempted to convert an auto_ptr serialization example into a unique_ptr example as compared to other unique_ptr examples on this site. Thus, why would I receive a compilation error deep within a Boost library? I have used Boost serialization on standard containers before with no problems and, although this is a custom adapter, if similar examples have compiled, why not this?
I referenced http://www.boost.org/doc/libs/1_51_0/libs/serialization/example/demo_auto_ptr.cpp in an attempt to serialize my binary tree using a custom adaptor. Below is a code dump representing an sscce.
// disables conversion from 'std::streamsize' to 'size_t', possible loss of data
#pragma warning(disable:4244)
#ifndef BINARY_SEARCH_TREE_H_
#define BINARY_SEARCH_TREE_H_
#include<functional>
#include<memory>
#include<fstream>
#include<boost/archive/binary_oarchive.hpp>
#include<boost/archive/binary_iarchive.hpp>
#define BST_FILE_NAME "tree.dat" // default filename used to save and load
namespace boost {
namespace serialization {
template <class Archive, class T>
inline void save
(Archive &archive,
const std::unique_ptr<T> &subtree,
const unsigned int file_version)
{
// only the raw pointer has to be saved
const T *const subtree_x = subtree.get();
archive << subtree_x;
}
template <class Archive, class T>
inline void load
(Archive &archive,
const std::unique_ptr<T> &subtree,
const unsigned int file_version)
{
T *p_subtree;
archive >> p_subtree;
#if BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB, == 1)
subtree.release();
subtree = std::unique_ptr< T >(p_subtree);
#else
subtree.reset(p_subtree);
#endif
}
template <class Archive, class T>
inline void serialize
(Archive &archive,
const std::unique_ptr<T> &subtree,
const unsigned int file_version)
{
boost::serialization::split_free(archive, subtree, file_version);
}
} // namespace serialization
} // namespace boost
template <class T = int>
class BinarySearchTree{
class BinarySearchTreeNode{
public:
std::unique_ptr<BinarySearchTreeNode> node_factory(const T &new_key, const T &new_index){
return std::unique_ptr<BinarySearchTreeNode>(new BinarySearchTreeNode(new_key, new_index)); }
BinarySearchTreeNode(BinarySearchTreeNode &&other) : key(other.key), index(other.index), left(std::move(other.left)),
right(std::move(other.right)) {key = index = left = right = nullptr; }
BinarySearchTreeNode &operator=(BinarySearchTreeNode &&rhs) { if(this != rhs) { key = rhs.key; index = rhs.index;
left = std::move(rhs.left); right = std::move(rhs.right);
rhs.key = rhs.index = rhs.left = rhs.right = nullptr;} return *this;}
~BinarySearchTreeNode() {} // Note to self; don't hide the destructor
friend class BinarySearchTree;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &archive, const unsigned int /* file_version */){
archive & key;
archive & index;
archive & left;
archive & right;
}
T key;
long index;
std::unique_ptr<BinarySearchTreeNode> left;
std::unique_ptr<BinarySearchTreeNode> right;
BinarySearchTreeNode() {}
BinarySearchTreeNode(const T &new_key, const T &new_index) :key(new_key), index(new_index),
left(nullptr), right(nullptr) {}
};
std::unique_ptr<BinarySearchTreeNode> root;
std::list<T> tree_keys;
std::list<long> tree_indicies;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &archive, const unsigned int version){
archive & root;
}
BinarySearchTree(const BinarySearchTree &other){}
BinarySearchTree &operator=(const BinarySearchTree &rhs){}
std::unique_ptr<BinarySearchTreeNode> insert(const T &new_key, const T &new_index,
std::unique_ptr<BinarySearchTreeNode> &tree){
if(tree == nullptr){
return root->node_factory(new_key, new_index);
}else if(std::less<T> () (new_key, tree->key)){ // Left insertion
tree->left = insert(new_key, new_index, tree->left);
return std::move(tree);
}else { // Right insertion
tree->right = insert(new_key, new_index, tree->right);
return std::move(tree);
}
}
public:
BinarySearchTree() : root(nullptr) {}
BinarySearchTree(BinarySearchTree &&other) : root(std::move(other.root)) { other.root = nullptr; }
BinarySearchTree &operator=(BinarySearchTree &&rhs) { if(this != rhs) { root = std::move(rhs.root);
rhs.root = nullptr} return *this; }
bool insert_into_tree(const T &new_key, const T &new_index){
if(new_key == NULL){
return false;
}
root = std::move(insert(new_key, new_index, root));
return true;
}
void save(const BinarySearchTree &tree)
{
// create and open a binary archive for output
std::ofstream writer(BST_FILE_NAME, std::ofstream::out | std::ofstream::binary);
if(writer){
boost::archive::binary_oarchive serial_writer(writer);
//set_flags(0, true);
// write class instance to archive
serial_writer << tree;
// archive and stream closed when destructors are called
}else if(writer.fail()){
writer.clear();
}
}
void load(BinarySearchTree &tree)
{
// create and open a binary archive for output
std::ifstream reader(BST_FILE_NAME, std::ifstream::in | std::ifstream::binary);
if(reader){
boost::archive::binary_iarchive serial_reader(reader);
// read class state from archive
serial_reader >> tree;
// archive and stream closed when destructors are called
}else if(reader.fail()){
reader.clear();
}
}
~BinarySearchTree() {}
};
#endif
The code above compiles as does any usage of the tree member functions. Once choosing to call save, that is when the compiler error appears. Here is main:
#include<cstdlib>
#include "physical_view.h"
using namespace std;
int main(int argc, char *argv[]){
BinarySearchTree<> tree;
tree.insert_into_tree(10, 5);
tree.insert_into_tree(5, 15);
tree.insert_into_tree(15, 10); <--------- All is wonderful to here!
tree.save(tree); <---------- Compiler unhappy here!
return EXIT_SUCCESS;
}
How about that compiler error:
Error 1 error C2039: 'serialize' : is not a member of 'std::unique_ptr<_Ty>' c:\boost_12\include\boost-1_53_1\boost\serialization\access.hpp 118
I thank you for helping me resolve this compiler error.

The is that the signature of your serialize and load functions is incorrect. The std::unique_ptr<T> argument needs to be non-const for both of them. Since it can't deduce the types (due to the const), it simply ignores the overload and fails to find it at all.

Related

How to implement Boost::Serialize for Boost::Nested_Container

(Followup of another question.)
Boost::Serialize often delivers an exception on oarchive, complaining that re-creating a particular object would result in duplicate objects. Some archives save and re-load successfully, but many result in the error above. I have not been able yet to determine the exact conditions under which the error occurs, but I have proven that none of the content used to populate the nested_container and the flat object list contains duplicate object IDs. I am using text archive, not binary. Here is how I have modified the code for nested_container and also for another, separate flat object list in order to do Boost::Serialize:
struct obj
{
int id;
const obj * parent = nullptr;
obj()
:id(-1)
{ }
obj(int object)
:id(object)
{ }
int getObjId() const
{
return id;
}
bool operator==(obj obj2)
{
if (this->getObjId() == obj2.getObjId())
return true;
else
return false;
}
#if 1
private:
friend class boost::serialization::access;
friend std::ostream & operator<<(std::ostream &os, const obj &obj);
template<class Archive>
void serialize(Archive &ar, const unsigned int file_version)
{
ar & id & parent;
}
#endif
};
struct subtree_obj
{
const obj & obj_;
subtree_obj(const obj & ob)
:obj_(ob)
{ }
#if 1
private:
friend class boost::serialization::access;
friend std::ostream & operator<<(std::ostream &os, const subtree_obj &obj);
template<class Archive>
void serialize(Archive &ar, const unsigned int file_version)
{
ar & obj_;
}
#endif
};
struct path
{
int id;
const path *next = nullptr;
path(int ID, const path *nex)
:id(ID), next(nex)
{ }
path(int ID)
:id(ID)
{ }
#if 1
private:
friend class boost::serialization::access;
friend std::ostream & operator<<(std::ostream &os, const path &pathe);
template<class Archive>
void serialize(Archive &ar, const unsigned int file_version)
{
ar & id & next;
}
#endif
};
struct subtree_path
{
const path & path_;
subtree_path(const path & path)
:path_(path)
{ }
#if 1
private:
friend class boost::serialization::access;
friend std::ostream & operator<<(std::ostream &os, const subtree_path &pathe);
template<class Archive>
void serialize(Archive &ar, const unsigned int file_version)
{
ar & path_;
}
#endif
};
//
// My flattened object list
//
struct HMIObj
{
int objId;
std::string objType;
HMIObj()
:objId(-1), objType("")
{ }
bool operator==(HMIObj obj2)
{
if (this->getObjId() == obj2.getObjId())
&& this->getObjType() == obj2.getObjType())
return true;
else
return false;
}
int getObjId() const
{
return objId;
}
std::string getObjType() const
{
return objType;
}
#if 1
private:
friend class boost::serialization::access;
friend std::ostream & operator<<(std::ostream &os, const HMIObj &obj);
template<class Archive>
void serialize(Archive &ar, const unsigned int file_version)
{
ar & objId & objType;
}
#endif
};
The problem you're experiencing is most likely due to, again, the particular order in which elements are traversed in index #0 (the hashed one). For instance, if we populate the container like this:
nested_container c;
c.insert({54});
auto it=c.insert({0}).first;
insert_under(c,it,{1});
Then the elements are listed in index #0 as (1, 54, 0). The crucial problem here is that 1 is a child of 0: when loading elements in the same order as they were saved, the first one is then 1, but this needs 0 to be loaded before in order to properly point to it. This is what Boost.Serialization very smartly detects and complains about. Such child-before-parent situations depend on the very unpredictable way elements are sorted in a hashed index, which is why you see the problem just sometimes.
You have two simple solutions:
Swap indices #0 and #1 in the definition of your nested container: as index #1 sorting order is the tree preorder, it is guaranteed that parents get processed before their children.
Override the serialization code of the nested container so as to go through index #1:
template<class Archive>
void serialize(Archive& ar,nested_container& c,unsigned int)
{
if constexpr(Archive::is_saving::value){
boost::serialization::stl::save_collection(ar,c.get<1>());
}
else{
boost::serialization::load_set_collection(ar,c.get<1>());
}
}
Complete demo code for solution #2 follows:
Live On Coliru
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
#include <iterator>
struct obj
{
int id;
const obj* parent=nullptr;
};
namespace boost{
namespace serialization{
template<class Archive>
void serialize(Archive& ar,obj& x,unsigned int)
{
ar&x.id&x.parent;
}
}} /* namespace boost::serialization */
struct subtree_obj
{
const obj& obj_;
};
struct path
{
int id;
const path* next=nullptr;
};
struct subtree_path
{
const path& path_;
};
inline bool operator<(const path& x,const path& y)
{
if(x.id<y.id)return true;
else if(y.id<x.id)return false;
else if(!x.next) return y.next;
else if(!y.next) return false;
else return *(x.next)<*(y.next);
}
inline bool operator<(const subtree_path& sx,const path& y)
{
const path& x=sx.path_;
if(x.id<y.id)return true;
else if(y.id<x.id)return false;
else if(!x.next) return false;
else if(!y.next) return false;
else return subtree_path{*(x.next)}<*(y.next);
}
inline bool operator<(const path& x,const subtree_path& sy)
{
return x<sy.path_;
}
struct obj_less
{
private:
template<typename F>
static auto apply_to_path(const obj& x,F f)
{
return apply_to_path(x.parent,path{x.id},f);
}
template<typename F>
static auto apply_to_path(const obj* px,const path& x,F f)
->decltype(f(x))
{
return !px?f(x):apply_to_path(px->parent,{px->id,&x},f);
}
public:
bool operator()(const obj& x,const obj& y)const
{
return apply_to_path(x,[&](const path& x){
return apply_to_path(y,[&](const path& y){
return x<y;
});
});
}
bool operator()(const subtree_obj& x,const obj& y)const
{
return apply_to_path(x.obj_,[&](const path& x){
return apply_to_path(y,[&](const path& y){
return subtree_path{x}<y;
});
});
}
bool operator()(const obj& x,const subtree_obj& y)const
{
return apply_to_path(x,[&](const path& x){
return apply_to_path(y.obj_,[&](const path& y){
return x<subtree_path{y};
});
});
}
};
using namespace boost::multi_index;
using nested_container=multi_index_container<
obj,
indexed_by<
hashed_unique<member<obj,int,&obj::id>>,
ordered_unique<identity<obj>,obj_less>
>
>;
#if 1 /* set to 0 to trigger pointer conflict exception */
#include <boost/serialization/set.hpp>
namespace boost{
namespace serialization{
template<class Archive>
void serialize(Archive& ar,nested_container& c,unsigned int)
{
if constexpr(Archive::is_saving::value){
boost::serialization::stl::save_collection(ar,c.get<1>());
}
else{
boost::serialization::load_set_collection(ar,c.get<1>());
}
}
}} /* namespace boost::serialization */
#endif
template<typename Iterator>
inline auto insert_under(nested_container& c,Iterator it,obj x)
{
x.parent=&*it;
return c.insert(std::move(x));
}
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <iostream>
#include <sstream>
void print(const nested_container& c)
{
for(const obj& x:c){
std::cout<<"("<<x.id;
if(x.parent)std::cout<<"->"<<x.parent->id;
std::cout<<")";
}
std::cout<<"\n";
}
int main()
{
nested_container c;
c.insert({54});
auto it=c.insert({0}).first;
insert_under(c,it,{1});
print(c);
std::ostringstream oss;
boost::archive::text_oarchive oa(oss);
oa<<c;
nested_container c2;
std::istringstream iss(oss.str());
boost::archive::text_iarchive ia(iss);
ia>>c2;
print(c2);
}
By the way, why are you providing serialize functions for subtree_obj, path and subtree_path? You don't need that to serialize nested_containers.

Hash Table not accepting function passed into constructor in member init list

I have a hash table template that I have written for a class. I have a project due that relies on utilizing this hash table. It accepts an unsigned integer value to initialize the number of buckets it has, as well as a hash function to point to. I have not written that hash function yet, but I have a declaration for it. When I try to use the member initializer in my Game class for the hash table data member, it gives me an error that I don't understand.
Error 1 error C3867: 'Game::xorHash': function call missing argument list; use '&Game::xorHash' to create a pointer to member
2 IntelliSense: no instance of constructor "HTable<Type>::HTable [with Type=std::string]" matches the argument list
argument types are: (int, unsigned int (const std::string &s))
my Hash Table class is as follows:
#pragma once
#include "SLList.h"
template<typename Type> class HTable
{
public:
HTable(unsigned int numOfBuckets, unsigned int (*hFunction) (const Type &v));
~HTable();
HTable<Type>& operator=(const HTable<Type>& that);
HTable(const HTable<Type>& that);
void insert(const Type& v);
bool findAndRemove(const Type& v);
void clear();
int find(const Type& v) const;
private:
SLList<Type>* ht;
unsigned int (*hFunct) (const Type &v);
unsigned int numOfBuck;
};
template<typename Type>
HTable<Type>::HTable(unsigned int numOfBuckets, unsigned int (*hFunction) (const Type &v))
{
ht = new SLList<Type>[numOfBuckets];
this->numOfBuck = numOfBuckets;
this->hFunct = hFunction;
}
template<typename Type>
HTable<Type>::~HTable()
{
delete [] ht;
ht = nullptr;
}
template<typename Type>
HTable<Type>& HTable<Type>::operator=(const HTable<Type>& that)
{
if(this != &that)
{
delete [] this->ht;
this->hFunct = that.hFunct;
this->numOfBuck = that.numOfBuck;
this->ht = new SLList<Type>[numOfBuck];
for(unsigned int i = 0; i < this->numOfBuck; i++)
this->ht[i] = that.ht[i];
}
return *this;
}
template<typename Type>
HTable<Type>::HTable(const HTable<Type>& that)
{
this = *that;
}
template<typename Type>
void HTable<Type>::insert(const Type& v)
{
ht[hFunct(v)].addHead(v);
}
template<typename Type>
bool HTable<Type>::findAndRemove(const Type& v)
{
SLLIter<Type> iter(ht[hFunct(v)]);
for(iter.begin(); !iter.end(); ++iter)
{
if(v == iter.current())
{
ht[hFunct(v)].remove(iter);
return true;
}
}
return false;
}
template<typename Type>
void HTable<Type>::clear()
{
for(unsigned int i = 0; i < this->numOfBuck; ++i)
ht[i].clear();
}
template<typename Type>
int HTable<Type>::find(const Type& v) const
{
SLLIter<Type> iter(ht[hFunct(v)]);
for(iter.begin(); !iter.end(); ++iter)
{
if(v == iter.current())
return hFunct(v);
}
return -1;
}
My Game.h:
#pragma once
#include "stdafx.h"
#include "HTable.h"
#include "BST.h"
#include "DTSTimer.h"
using namespace std;
class Game
{
public:
Game(void);
virtual ~Game(void);
void refresh();
void input();
unsigned int xorHash(const string &s);
private:
string userInput;
DTSTimer timer;
BST<string> answers;
HTable<string> dictionary;
};
My Game.cpp (this is obviously just a skeleton, since I can't get the member init to work)
#include "Game.h"
Game::Game(void) : dictionary(2048, xorHash)
{
}
Game::~Game(void)
{
}
void Game::refresh()
{
}
void Game::input()
{
}
unsigned int Game::xorHash(const string &s)
{
return 0;
}
I've been working on this for a good while, and have been hitting a wall. I would really appreciate some help on how to get this thing up and running. Let me know if there is another snippet that needs to be seen (I've tried to be thorough in that regard).
You have two problems. The first is that you don't pass the member function pointer properly (the error message tells you exactly what do do). The other problem is that a function pointer is not the same as a member function pointer.
A member function pointer needs an instance object object to call the member function on. And this instance is passed as a hidden first argument, something that normal functions don't have.
For this you might instead turn to std::function and std::bind:
class HTable
{
public:
HTable(unsigned int numOfBuckets, std::function<unsigned int(const Type&)> hFunction);
...
private:
std::function<unsigned int(const Type&)> hFunct;
...
};
Then
Game::Game(void) : dictionary(2048, std::bind(&Game::xorHash, this))
{
}

boost::archive_exception causes unhandled exception within VisualStudio2012

A boost::archive_exception is confounding me. The exception details are as follows
Unhandled exception: boost::archive::archive_exception at memory location...
This occurs when attempting the following boost::serialization load operation that essentially works when BinarySearchTree<std::string> but not when BinarySearchTree<int>
typedef boost::variant<BinarySearchTree<std::string>, BinarySearchTree<int>> BinarySearchTreeVariant;
BinarySearchTreeVariant search_tree_;
BinarySearchTree<std::string> string_search_tree_;
BinarySearchTree<int> int_search_tree_;
boost::shared_ptr<BinarySearchTree<std::string>> binary_string_search_tree_;
boost::shared_ptr<BinarySearchTree<int>> binary_int_search_tree_;
search_tree_ = int_search_tree_;
binary_int_search_tree_.reset(new BinarySearchTree<int>());
try{
if(!binary_int_search_tree_->load(binary_int_search_tree_, search_tree_name))
throw CustomException("Load of binary search tree to disk fail");
}
catch(CustomException &custom_exception){ }
The code that defines load is as follows
bool load(boost::shared_ptr<BinarySearchTree> &tree, const std::string &search_tree_file_name)
{
// create and open an archive for output
std::ifstream reader(search_tree_file_name);
if(reader){
boost::archive::text_iarchive serial_reader(reader);
// read class state from archive
serial_reader >> *tree; <<<--- Unhandled Exception
// archive and stream closed when destructors are called
}else if(reader.fail()){
reader.clear();
}
return true;
}
My search tree is defined with boost::serialization as follows
class BinarySearchTree{
private:
class BinarySearchTreeNode{
public:
friend class BinarySearchTree;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &archive, const unsigned int /* file_version */){
archive & key;
archive & index;
archive & left;
archive & right;
}
T key;
long index;
boost::shared_ptr<BinarySearchTreeNode> left;
boost::shared_ptr<BinarySearchTreeNode> right;
}; // End Tree Node Class Definition
boost::shared_ptr<BinarySearchTreeNode> root;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &archive, const unsigned int version){
archive & root;
}
};

Able to serialize with boost but unable to deserialize std::shared_ptr

boost serialization
namespace boost {
namespace serialization {
template <class Archive, class T>
inline void save
(Archive &archive,
const std::shared_ptr<T> subtree,
const unsigned int file_version)
{
// only the raw pointer has to be saved
const T *const subtree_x = subtree.get();
archive << subtree_x;
}
template <class Archive, class T>
inline void load
(Archive &archive,
std::shared_ptr<T> subtree,
const unsigned int file_version)
{
T *p_subtree;
archive >> p_subtree;
#if BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB, == 1)
subtree.release();
subtree = std::shared_ptr< T >(p_subtree);
#else
subtree.reset(p_subtree);
#endif
}
template <class Archive, class T>
inline void serialize
(Archive &archive,
std::shared_ptr<T> subtree, // no const or else get compile-time error
const unsigned int file_version)
{
boost::serialization::split_free(archive, subtree, file_version);
}
} // namespace serialization
} // namespace boost
tree class
class Tree{
private:
class TreeNode{
public:
std::shared_ptr<TreeNode> node_factory(const T &new_key, const long &new_index)
{
return std::shared_ptr<TreeNode>(new TreeNode(new_key, new_index));
}
friend class Tree;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &archive, const unsigned int /* file_version */){
archive & key;
archive & index;
archive & left;
archive & right;
}
T key;
long index;
std::shared_ptr<TreeNode> left;
std::shared_ptr<TreeNode> right;
}; // End Tree Node Class Definition
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &archive, const unsigned int version){
archive & root;
}
};
writer
bool save(std::shared_ptr<Tree> &tree, const std::string &search_tree_file_name)
{
// create and open a binary archive for output
std::ofstream writer(search_tree_file_name, std::ofstream::out | std::ofstream::binary);
if(writer){
boost::archive::binary_oarchive serial_writer(writer);
//set_flags(0, true);
// write class instance to archive
serial_writer << *tree;
// archive and stream closed when destructors are called
}else if(writer.fail()){
writer.clear();
}
return true;
}
reader
enter code here bool load(std::shared_ptr<Tree> &tree, const std::string &search_tree_file_name)
{
// create and open a binary archive for output
std::ifstream reader(search_tree_file_name, std::ifstream::in | std::ifstream::binary);
if(reader){
boost::archive::binary_iarchive serial_reader(reader);
// read class state from archive
serial_reader >> *tree;
// archive and stream closed when destructors are called
}else if(reader.fail()){
reader.clear();
}
return true;
}
I have written to and verified the successful serialization to a file but fail to deserialize from and into a usable object.
Whether I am writing in text or binary, I can verify the serialized output is correct but, for some reason, the serialize output does not deserialize and I am left with an empty object when loading.
Have a look at these links, might provide you some clue.
http://www.boost.org/doc/libs/1_49_0/libs/serialization/doc/shared_ptr.html
& http://www.boost.org/doc/libs/1_49_0/libs/serialization/doc/shared_ptr2.html
Although #Arun provided great documentation references useful for utilizing std::shared_ptr, I instead chose to employ boost::shared_ptr with boost::serialization and it has cured my de-serialization problem.

C++ templates problem

I have defined a generic tree-node class like this:
template<class DataType>
class GenericNode
{
public:
GenericNode() {}
GenericNode(const DataType & inData) : mData(inData){}
const DataType & data() const
{ return mData; }
void setData(const DataType & inData)
{ mData = inData; }
size_t getChildCount() const
{ return mChildren.size(); }
const GenericNode * getChild(size_t idx) const
{ return mChildren[idx]; }
GenericNode * getChild(size_t idx)
{ return mChildren[idx]; }
void addChild(GenericNode * inItem)
{ mChildren.push_back(inItem); }
private:
DataType mData;
typedef std::vector<GenericNode*> Children;
Children mChildren;
};
typedef GenericNode<std::string> TreeItemInfo;
And I would like to make it more generic by making the child pointer type customizable. For example to allow using a smart pointer type. Naively I tried this:
template<class DataType, class ChildPtr>
class GenericNode
{
public:
GenericNode() {}
GenericNode(const DataType & inData) : mData(inData){}
const DataType & data() const
{ return mData; }
void setData(const DataType & inData)
{ mData = inData; }
size_t getChildCount() const
{ return mChildren.size(); }
const ChildPtr getChild(size_t idx) const
{ return mChildren[idx]; }
ChildPtr getChild(size_t idx)
{ return mChildren[idx]; }
void addChild(ChildPtr inItem)
{ mChildren.push_back(inItem); }
private:
DataType mData;
typedef std::vector<ChildPtr> Children;
Children mChildren;
};
typedef GenericNode<std::string, GenericNode<std::string > * > TreeItemInfo;
However, that doesn't work of course because I need to specify the second parameter for the second parameter for the second parameter etc... into eternity.
Is there a way to solve this puzzle?
EDIT
I found a solution based on #Asaf's answer. For those interested, below is a full code sample (comments are welcome).
EDIT2
I modified the interface so that externally always raw pointers are used.
#include <string>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <assert.h>
template <class PointeeType>
struct NormalPointerPolicy
{
typedef PointeeType* PointerType;
static PointeeType* getRaw(PointerType p)
{
return p;
}
};
template <class PointeeType>
struct SharedPointerPolicy
{
typedef boost::shared_ptr<PointeeType> PointerType;
static PointeeType* getRaw(PointerType p)
{
return p.get();
}
};
template <class DataType, template <class> class PointerPolicy>
class GenericNode
{
public:
GenericNode() { }
GenericNode(const DataType & inData) : mData(inData) { }
typedef GenericNode<DataType, PointerPolicy> This;
typedef typename PointerPolicy<This>::PointerType ChildPtr;
const This * getChild(size_t idx) const
{ return PointerPolicy<This>::getRaw(mChildren[idx]); }
This * getChild(size_t idx)
{ return PointerPolicy<This>::getRaw(mChildren[idx]); }
void addChild(This * inItem)
{
ChildPtr item(inItem);
mChildren.push_back(item);
}
const DataType & data() const
{ return mData; }
void setData(const DataType & inData)
{ mData = inData; }
private:
DataType mData;
std::vector<ChildPtr> mChildren;
};
typedef GenericNode<std::string, NormalPointerPolicy> SimpleNode;
typedef GenericNode<std::string, SharedPointerPolicy> SmartNode;
int main()
{
SimpleNode simpleNode;
simpleNode.addChild(new SimpleNode("test1"));
simpleNode.addChild(new SimpleNode("test2"));
SimpleNode * a = simpleNode.getChild(0);
assert(a->data() == "test1");
const SimpleNode * b = static_cast<const SimpleNode>(simpleNode).getChild(1);
assert(b->data() == "test2");
SmartNode smartNode;
smartNode.addChild(new SmartNode("test3"));
smartNode.addChild(new SmartNode("test4"));
SmartNode * c = smartNode.getChild(0);
assert(c->data() == "test3");
SmartNode * d = static_cast<const SmartNode>(smartNode).getChild(1);
assert(d->data() == "test4");
return 0;
}
Not the way you look at it. You should combine some kind of inheritance here.
Try this, for example:
template <class PointeeType>
struct NormalPointerPolicy
{
typedef PointeeType* PointerType;
};
template <class PointeeType>
struct SmartPointerPolicy
{
typedef MySmartPtrClass<PointeeType> PointerType;
};
template <class DataType>
class BaseGenericNode
{
public:
BaseGenericNode() {}
BaseGenericNode(const DataType & inData) : mData(inData){}
const DataType & data() const
{ return mData; }
void setData(const DataType & inData)
{ mData = inData; }
protected:
DataType mData;
};
template <class DataType, template <class> class PointerPolicy>
class GenericNode : public BaseGenericNode<DataType>
{
typedef typename PointerPolicy<BaseGenericNode<DataType> >::PointerType ChildPtr;
private:
typedef std::vector<ChildPtr> Children;
Children mChildren;
};
The GenericNode is the actual node type, which holds the base type 'BaseGenericNode'.
The base type holds the actual data (and its related functionality), and the derived class holds the links to other nodes.
There are 2 template policy classes for how your pointer actually looks like, and you use them like this:
GenericNode<int, NormalPointerPolicy> instance;
GenericNode<int, SmartPointerPolicy> instance;
The problem (or advantage?) of this implementation is that a node with pointers of one kind, can hold child nodes with pointers of another kind.