#include <iostream>
using namespace std;
template <typename E1, typename E2>
class Mix : public E1, public E2
{
public:
Mix() : E1(1), E2(2)
{
// Set nothing here
cerr << "This is " << this << " in Mix" << endl;
print(cerr);
}
void print(ostream& os)
{
os << "E1: " << E1::e1 << ", E2: " << E2::e2 << endl;
// os << "E1: " << e1 << ", E2: " << e2 << endl; won't compile
}
};
class Element1
{
public:
Element1(unsigned int e) : e1(e)
{
cerr << "This is " << this << " in Element1" << endl;
}
unsigned int e1;
};
class Element2
{
public:
Element2(unsigned int e) : e2(e)
{
cerr << "This is " << this << " in Element2" << endl;
}
unsigned int e2;
};
int main(int argc, char** argv)
{
Mix<Element1, Element2> m;
}
Now, since we're equally inheriting from the two template parameter classes, I would expect this to be the same in the two constructors, but this is not the case. Here is the run log:
This is 0x7fff6c04aa70 in Element1
This is 0x7fff6c04aa74 in Element2
This is 0x7fff6c04aa70 in Mix
E1: 1, E2: 2
As you can see, while this is the same in Element1 and Mix, this is not true for Element2. Why is that? Also, I would expect to have access to e1 and e2 from the base classes. Can you explain this behavior?
The Element Mix contains an Element1 and an Element2. These are - perhaps implementation specifically aligned - written after one another in memory. If you use Mix as Element1, this will point to the first of the two (with size of Element1), if you use it as Element2 it will point to the second (with size of Element2) and if you use it as Mix it will point to the base address, which is the same as Element1s base address, but has a differenz size (at least size of Element1 + size of Element2).
Edit: You can verify this by outputting the size too:
#include
using namespace std;
template <typename E1, typename E2>
class Mix : public E1, public E2
{
public:
Mix() : E1(1), E2(2)
{
// Set nothing here
cerr << "This is " << this << " + " << sizeof(*this) << " in Mix" << endl;
print(cerr);
}
void print(ostream& os)
{
os << "E1: " << E1::e1 << ", E2: " << E2::e2 << endl;
// os << "E1: " << e1 << ", E2: " << e2 << endl; won't compile
}
};
class Element1
{
public:
Element1(unsigned int e) : e1(e)
{
cerr << "This is " << this << " + " << sizeof(*this) << " in Element1" << endl;
}
unsigned int e1;
};
class Element2
{
public:
Element2(unsigned int e) : e2(e)
{
cerr << "This is " << this << " + " << sizeof(*this) << " in Element2" << endl;
}
unsigned int e2;
};
int main(int argc, char** argv)
{
Mix<Element1, Element2> m;
}
Output:
This is 0x7fffc9cad310 + 4 in Element1
This is 0x7fffc9cad314 + 4 in Element2
This is 0x7fffc9cad310 + 8 in Mix
E1: 1, E2: 2
Related
I wrote a generic class for handling and executing a function pointer. This is a simplified equivalent of std::function and std::bind. To handle member functions I use cast to internal EventHandler::Class type. Question: is it ok to cast it that way? Will it work in all cases when invoking handled function?
template <typename ReturnType, typename... Arguments>
class EventHandler
{
class Class {};
ReturnType (Class::*memberFunction)(Arguments...) = nullptr;
union {
Class *owner;
ReturnType(*function)(Arguments...) = nullptr;
};
public:
EventHandler() = default;
EventHandler(EventHandler &&) = default;
EventHandler(const EventHandler &) = default;
EventHandler &operator=(EventHandler &&) = default;
EventHandler &operator=(const EventHandler &) = default;
EventHandler(ReturnType (*function)(Arguments...)) :
function(function)
{
}
template <typename Owner>
EventHandler(Owner *owner, ReturnType (Owner::*memberFunction)(Arguments...)) :
memberFunction((ReturnType (Class::*)(Arguments...)) memberFunction),
owner((Class *) owner)
{
}
template <typename Owner>
EventHandler(const Owner *owner, ReturnType (Owner::*memberFunction)(Arguments...) const) :
memberFunction((ReturnType (Class::*)(Arguments...)) memberFunction),
owner((Class *) owner)
{
}
ReturnType operator()(Arguments... arguments)
{
return memberFunction ?
(owner ? (owner->*memberFunction)(arguments...) : ReturnType()) :
(function ? function(arguments...) : ReturnType());
}
};
The implementation provides handle for a global function, a member function and a const member function. Obviously there is volatile and const volatile that is not show here for clarity.
EDIT
All the code below is just a representation of all of kinds of supported functions.
class Object
{
public:
double y = 1000;
Object() = default;
Object(double y) : y(y) {}
static void s1(void) { std::cout << "s1()" << std::endl; }
static void s2(int a) { std::cout << "s2(a:" << 10 + a << ")" << std::endl; }
static void s3(int a, float b) { std::cout << "s3(a:" << 10 + a << ", b:" << 10 + b << ")" << std::endl; }
static int s4(void) { std::cout << "s4(): "; return 10 + 4; }
static Object s5(int a) { std::cout << "s5(a:" << 10 + a << "): "; return Object(10 + 5.1); }
static float s6(int a, Object b) { std::cout << "s6(a:" << 10 + a << ", b:" << 10 + b.y << "); "; return 10 + 6.2f; }
void m1(void) { std::cout << "m1()" << std::endl; }
void m2(int a) { std::cout << "m2(a:" << y + a << ")" << std::endl; }
void m3(int a, float b) { std::cout << "m3(a:" << y + a << ", b:" << y + b << ")" << std::endl; }
int m4(void) { std::cout << "m4(): "; return ((int) y) + 4; }
Object m5(int a) { std::cout << "m5(a:" << y + a << "): "; return Object(y + 5.1); }
float m6(int a, Object b) { std::cout << "m6(a:" << y + a << ", b:" << y + b.y << "); "; return ((int) y) + 6.2f; }
void c1(void) const { std::cout << "c1()" << std::endl; }
void c2(int a) const { std::cout << "c2(a:" << y + a << ")" << std::endl; }
void c3(int a, float b) const { std::cout << "c3(a:" << y + a << ", b:" << y + b << ")" << std::endl; }
int c4(void) const { std::cout << "c4(): "; return ((int) y) + 4; }
Object c5(int a) const { std::cout << "c5(a:" << y + a << "): "; return Object(y + 5.1); }
float c6(int a, Object b) const { std::cout << "c6(a:" << y + a << ", b:" << y + b.y << "); "; return ((int) y) + 6.2f; }
};
void f1(void) { std::cout << "f1()" << std::endl; }
void f2(int a) { std::cout << "f2(a:" << a << ")" << std::endl; }
void f3(int a, float b) { std::cout << "f3(a:" << a << ", b:" << b << ")" << std::endl; }
int f4(void) { std::cout << "f4(): "; return 4; }
Object f5(int a) { std::cout << "f5(a:" << a << "): "; return Object(5.1); }
float f6(int a, Object b) { std::cout << "f6(a:" << a << ", b:" << b.y << "); "; return 6.2f; }
Here is the usage example for all of the above functions
int main()
{
std::cout << "=== Global functions" << std::endl;
EventHandler ef1(f1); ef1();
EventHandler ef2(f2); ef2(2);
EventHandler ef3(f3); ef3(3, 3.1f);
EventHandler ef4(f4); std::cout << ef4() << std::endl;
EventHandler ef5(f5); std::cout << ef5(5).y << std::endl;
EventHandler ef6(f6); std::cout << ef6(6, Object(6.1)) << std::endl;
std::cout << std::endl;
std::cout << "=== Member static functions" << std::endl;
EventHandler es1(Object::s1); es1();
EventHandler es2(Object::s2); es2(2);
EventHandler es3(Object::s3); es3(3, 3.1f);
EventHandler es4(Object::s4); std::cout << es4() << std::endl;
EventHandler es5(Object::s5); std::cout << es5(5).y << std::endl;
EventHandler es6(Object::s6); std::cout << es6(6, Object(6.1)) << std::endl;
std::cout << std::endl;
std::cout << "=== Member functions" << std::endl;
Object object(20);
EventHandler em1(&object, &Object::m1); em1();
EventHandler em2(&object, &Object::m2); em2(2);
EventHandler em3(&object, &Object::m3); em3(3, 3.1f);
EventHandler em4(&object, &Object::m4); std::cout << em4() << std::endl;
EventHandler em5(&object, &Object::m5); std::cout << em5(5).y << std::endl;
EventHandler em6(&object, &Object::m6); std::cout << em6(6, Object(6.1)) << std::endl;
std::cout << std::endl;
std::cout << "=== Member const functions" << std::endl;
const Object constObject(30);
EventHandler ec1(&constObject, &Object::c1); ec1();
EventHandler ec2(&constObject, &Object::c2); ec2(2);
EventHandler ec3(&constObject, &Object::c3); ec3(3, 3.1f);
EventHandler ec4(&constObject, &Object::c4); std::cout << ec4() << std::endl;
EventHandler ec5(&constObject, &Object::c5); std::cout << ec5(5).y << std::endl;
EventHandler ec6(&constObject, &Object::c6); std::cout << ec6(6, Object(6.1)) << std::endl;
system("pause");
return 0;
}
Finally - to the point - here an example that shows how much easier in use is the EventHandler I prepared when compared to std::function interface. And actually the reason of such approach.
EventHandler<float, int, Object> example;
example = f6;
example(7, Object(7.1));
example = EventHandler(&object, &Object::m6);;
example(8, Object(8.1));
It’s undefined behavior to call a function through a function pointer(-to-member) of a different type. (Some practical reasons for this rule are that the object’s address might need to be adjusted to call a member function of a base class or that a vtable might be involved.) You can use type erasure to allow calling member functions on objects of different types (which is what std::bind does), or you can (restrict to member functions and) add the class type as a template parameter.
Of course, the usual answer is to just use std::function with a lambda that captures the object in question and calls whatever member function. You can also take the C approach and define various functions with a void* parameter that cast that parameter to a known class type and call the desired member function.
I have a class that I use with the Armadillo package to create a specific kind of matrix. I'm having trouble debugging it, so I would like to use a function I have written called Matlab_Print. It lives in its own .h and .cpp file and is used throughout my code. The class and the function both work perfectly, but I do not seem to be able to combine them.
I have tried #include "Matlab_Print" in SU3.h both before and after the class definition. I really don't want to make the function a class function as I use Matlab_Print frequently. I do have a workaround but it is inconvenient, and at any rate I am looking at this as a learning opportunity.
I trap error messages with a try when calling the SU3 constructor and I get the following:
error: Mat::init(): size is fixed and hence cannot be changed
main.cpp
#include "pch.h"
#include <new>
#include <exception>
#include "SU3.h"
int main(int argc, char *argv[])
{
int icount { 0 };
SU3 *su3[10];
try
{
for (icount = 0; icount < 10; icount++)
{
su3[icount] = new SU3(0.1);
}
}
catch (int param) { cout << "Function " << __func__ << " int " << param << " exception in memory allocation for su3" << std::endl; exit(1); }
catch (char param) { cout << "Function " << __func__ << " char " << param << " exception in memory allocation for su3" << std::endl; exit(1); }
catch (...) { cout << "Function " << __func__ << " exception in memory allocation for su3" << std::endl; exit(1); }
return 0;
}
SU3.h
#include "pch.h"
#include "SU3.h"
#include <armadillo>
#include "Matlab_Print.h"
class SU3
{
public:
arma::Mat<cx_double>::fixed<3, 3> *X;
SU3(const double epsilon);
};
SU3.cpp
SU3::SU3(const double epsilon) // simplifed so that epsilon plays no role
{
const std::complex<double> o{ 1.0 , 0.0 }; // complex 1
const std::complex<double> z{ 0.0 , 1.0 }; // complex 0
X = new arma::Mat<cx_double>::fixed<3, 3>{ fill::zeros }; //// solution to problem: define and initialize pointer ////
*X = { { o, z, z},
{ z, o, z},
{ z, z, o} };
Matlab_Print(*X, "SU3"); // this is the line I wish to use
}
Matlab_Print.h
#include <armadillo>
#include <complex>
void Matlab_Print(arma::Mat<cx_double>::fixed<3, 3> Matrix, std::string T);
Matlab_Print.cpp
#include "pch.h"
#include "Matlab_Print.h"
void Matlab_Print(arma::Mat<cx_double>::fixed<3, 3> Matrix, std::string T)
{
std::cout << std::endl;
std::cout << "RE = [" << std::real(Matrix(0, 0)) << " " << std::real(Matrix(0, 1)) << " " << std::real(Matrix(0, 2)) << "; ";
std::cout << std::real(Matrix(1, 0)) << " " << std::real(Matrix(1, 1)) << " " << std::real(Matrix(1, 2)) << "; ";
std::cout << std::real(Matrix(2, 0)) << " " << std::real(Matrix(2, 1)) << " " << std::real(Matrix(2, 2)) << "]; " << std::endl;
std::cout << "IM = [" << std::imag(Matrix(0, 0)) << " " << std::imag(Matrix(0, 1)) << " " << std::imag(Matrix(0, 2)) << "; ";
std::cout << std::imag(Matrix(1, 0)) << " " << std::imag(Matrix(1, 1)) << " " << std::imag(Matrix(1, 2)) << "; ";
std::cout << std::imag(Matrix(2, 0)) << " " << std::imag(Matrix(2, 1)) << " " << std::imag(Matrix(2, 2)) << "]; " << std::endl;
std::cout << T << " = RE + 1i*IM;" << std::endl;
}
Thank you for your patience. I hope this is all of the information you need.
As #uneven_mark notes, you have undefined behavior in SU3::SU3, because you are dereferencing X without initializing it first. You probably don't want a pointer here.
N.b. you don't need new to create objects of class type.
class SU3
{
public:
arma::Mat<cx_double>::fixed<3, 3> X;
SU3(const double epsilon);
};
using namespace std::literals::complex_literals;
SU3::SU3(const double epsilon)
: X({ { 1, 1i, 1i },
{ 1i, 1, 1i },
{ 1i, 1i, 1 } }) // prefer member initialisers over assingments
{
Matlab_Print(X, "SU3");
}
I am creating observer template sample in C++ on windows.
Here there is an agent which has a list of customers. Whenever an entity(variable x) of the agent changes it notifies its customers about the same and passes the value of x to customers. The customers then store this value in their respective variables.
In the below code the agent acts as subject and the customers act as observers.
The agents are created from their agent template class and the customers are created from their customer template class.
template <typename T>
class customer // acts as base observer class
{
char name[50];
public:
customer()
{
cout << __FUNCTION__ "(): " << "DEFAULT CONS\n";
}
customer(char* nm)
{
strcpy_s(name, nm);
cout << __FUNCTION__ "(): " << "name set to " << name << "\n";
}
char * getName()
{
return(name);
}
virtual void update(int c)
{
}
};
class customerC: public customer<customerC>
{
int c;
public:
customerC()
{
cout << __FUNCTION__ "(): " << "DEFAULT customerc cons\n";
}
customerC(char* nm):customer<customerC>(nm)
{
cout << __FUNCTION__ "(): " << "customer is " << getName() << "\n";
}
void update(int val)
{
cout << __FUNCTION__ "(): c to " << c << "\n";
c = val;
}
};
class customerD: public customer<customerD>
{
int d;
public:
customerD()
{
cout << __FUNCTION__ "(): " << "DEFAULT customerd cons\n";
}
customerD(char* nm):customer<customerD>(nm)
{
cout << __FUNCTION__ "(): " << "customer is " << getName() << "\n";
}
void update(int val)
{
cout << __FUNCTION__ "(): c to " << d << "\n";
d = val;
}
};
template<typename T>
class agent
{
char name[50];
int x;
protected:
vector<customer<T>*> custList;
public:
agent()
{
cout << __FUNCTION__ "(): " << "DEFAULT agent cons\n";
}
virtual void setx(int c)
{
cout << __FUNCTION__ "(): " << "Setting x to " << c << "\n";
//// x = c;
//// notifyObs();
}
virtual void getx()
{
cout << __FUNCTION__ "(): " << "x = " << x << "\n";
}
void addCust(customer<T>* cobj)
{
cout << __FUNCTION__ "(): " << "Adding customer " << cobj->getName() << " to list.\n";
custList.push_back(cobj);
}
void showCust()
{
cout << __FUNCTION__ "(): " << "Customers are:\n";
if(custList.empty())
cout << "\n\nYou have no items.";
else
{
vector<customer<T>*>::iterator cs;
for(cs = custList.begin(); cs != custList.end(); ++cs)
{
cout << (*cs)->getName() << "\n";
}
}
}
int notifyObs()
{
cout << __FUNCTION__ "(): " << "Customers notified are:\n";
if(custList.empty())
cout << "\n\nYou have no items.";
else
{
vector<customer<T>*>::iterator cs;
for(cs = custList.begin(); cs != custList.end(); ++cs)
{
cout << (*cs)->getName() << "\n";
(*cs)->update(x);
}
}
return 0;
}
};
class agentS: public agent<agentS>
{
int x;
public:
agentS()
{
cout << __FUNCTION__ "(): " << "DEFAULT agentS cons\n";
}
void setx(int c)
{
cout << __FUNCTION__ "(): " << "Setting x to " << c << "\n";
x = c;
notifyObs();
}
void getx()
{
cout << __FUNCTION__ "(): " << "x = " << x << "\n";
}
};
int _tmain(int argc, _TCHAR* argv[])
{
customerC cobj("c1");
customerD dobj("c2");
agentS agS;
agS.addCust(cobj);
//// agS.addCust<customer<customerC>>(cobj);
//// agS.addCust(dobj);
agS.showCust();
agS.setx(4);
return(0);
}
I get compilation error
error C2664: 'agent<T>::addCust' : cannot convert parameter 1 from 'customerC' to 'customer<T> *'
I know the way I have called addCust is wrong but still not getting any idea as to call it.
Any hint to resolve this issue?
Also is the way I have created agents class correct?
class agentS: public agent<agentS>
When I call addCust() function I pass observer objects.
By creating your agentS class that way, the effective signature for addCust becomes void addCust(customer<agentS>* cobj);. However, your customer classes are not templated on the agent type (there doesn't actually seem to be a reason for it to be templated).
You appear to be mixing dynamic polymorphism (inheritance and virtual functions with customer) and static polymorphism (templates to create a vector of one type of customer). Either of these options on their own would make more sense:
Dynamic polymorphism (inheritance). You can store different types of customer in the same container, by storing the base class pointer, and use the customer base class and virtual functions to tread them in the same way:
struct customer {};
struct customerC : customer {};
struct customerD : customer {};
struct agent
{
void addCust(customer* customer) { ... }
std::vector<customer*> custList;
};
int main()
{
agent a;
customerC c;
a.addCust(&c);
}
Static polymorphism (templates). The agent class is templated on the customer type, so the vector can only contain one type of customer, but it's easy to create a specific agent for any given customer type:
struct customer {};
struct customerC : customer {};
struct customerD : customer {};
template<CustomerT>
struct agent
{
void addCust(CustomerT* customer) { ... }
std::vector<CustomerT*> custList;
};
int main()
{
agent<customerC> a;
customerC c;
a.addCust(&c);
}
I have four classes (A,B,C and D) following the classic diamond pattern and a Container class containing a unique_ptr<A>. I want to serialize these classes using the cereal serialization library.
struct A {int f1; int f2; int f3}
struct B : public virtual A {
template<typename Archive>
inline void save(Archive& ar) const {
std::cerr << "Saving Obj: " << this << std::endl;
std::cerr << "This: " << &(this->f1) << " "
<< &(this->f2) << " " << &(this->f3) << std::endl;
std::cerr << "This: " << this->f1 << " "
<< this->f2 << " " << this->f3 << std::endl;
};
}
};
struct C : public virtual A {};
struct D : public B, public C {};
#include <cereal/archives/binary.hpp>
CEREAL_REGISTER_TYPE(B);
CEREAL_REGISTER_TYPE(C);
CEREAL_REGISTER_TYPE(D);
struct Container {
std::unique_ptr<A> obj;
template<typename Archive>
inline void save(Archive& ar) const {
std::cerr << "Saving Container" << std::endl;
std::cerr << "Obj Addr: " << obj.get() << std::endl;
std::cerr << "Obj: " << &(obj->f1) << " " << &(obj->f2)
<< " " << &(pq->f3) << std::endl;
std::cerr << "Obj: " << " " << pq->sq_count << " " << pq->sq_bits
<< " " << pq->dim << std::endl;
ar(obj); // Call serialization for obj, ie B.save(...)
}
}
All classes have cereal save and load functions, but I only included them for B and Container, as they are the only ones used in this example.
I use these classes as follows :
std::unique_ptr<A> obj(new B);
obj->f1 = 8;
obj->f2 = 8;
obj->f3 = 128;
std::unique_ptr<Container> db(new Container);
db.obj = std::move(obj);
std::ofstream out_file(out_filename);
cereal::BinaryOutputArchive out_archive(out_file);
out_archive(db);
And I get the following output:
Saving Container
Obj Addr: 0x23d2128
Obj: 0x23d2130 0x23d2134 0x23d2138 // Fields adresses (f1,f2,f3)
Obj: 8 8 128 // Fields values
Saving Obj: 0x23d2128 // Same object
This: 0x23d2118 0x23d211c 0x23d2120 // Different field adresses !
This: 4293296 0 37569440 // Garbage
My question is: Is it likely that this is a bug in cereal, or is there something that I don't get with virtual inheritance ?
Is it expected that the addresses of the fields of a given object ever change in a C++ program ?
I can't reproduce your error on the current develop branch of cereal, however I can reproduce it on the current master (1.1.2). I modified your code to actually compile:
#include <cereal/types/memory.hpp>
#include <cereal/types/polymorphic.hpp>
#include <cereal/archives/json.hpp>
#include <fstream>
#include <iostream>
struct A {
int f1; int f2; int f3;
virtual ~A() {}
template<typename Archive>
void serialize( Archive & ar )
{
std::cerr << "Saving A Obj: " << this << std::endl;
std::cerr << "This: " << &(this->f1) << " "
<< &(this->f2) << " " << &(this->f3) << std::endl;
std::cerr << "This: " << this->f1 << " "
<< this->f2 << " " << this->f3 << std::endl;
};
};
struct B : public virtual A {
template <class Archive>
void serialize( Archive & ar )
{
std::cerr << "Saving B Obj: " << this << std::endl;
std::cerr << "This: " << &(this->f1) << " "
<< &(this->f2) << " " << &(this->f3) << std::endl;
std::cerr << "This: " << this->f1 << " "
<< this->f2 << " " << this->f3 << std::endl;
ar( cereal::virtual_base_class<A>( this ) );
}
virtual ~B() {}
};
CEREAL_REGISTER_TYPE(B);
struct Container {
std::unique_ptr<A> obj;
template<typename Archive>
void serialize( Archive & ar )
{
std::cerr << "Saving Container (A)" << std::endl;
std::cerr << "Obj Addr: " << obj.get() << std::endl;
std::cerr << "Obj: " << &(obj->f1) << " " << &(obj->f2)
<< " " << &(obj->f3) << std::endl;
ar(obj); // Call serialization for obj, ie B.save(...)
}
};
int main()
{
std::unique_ptr<A> ptr(new B());
ptr->f1 = 8;
ptr->f2 = 8;
ptr->f3 = 128;
std::unique_ptr<Container> db(new Container());
db->obj = std::move(ptr);
std::stringstream ss;
{
cereal::JSONOutputArchive out_archive(ss);
out_archive(db);
}
std::cout << ss.str() << std::endl;
}
The output with 1.1.2:
Saving Container (A)
Obj Addr: 0x1738d78
Obj: 0x1738d80 0x1738d84 0x1738d88
Saving B Obj: 0x1738d78
This: 0x1738d78 0x1738d7c 0x1738d80
This: 4316664 0 8
Saving A Obj: 0x1738d70
This: 0x1738d78 0x1738d7c 0x1738d80
This: 4316664 0 8
{
"value0": {
"ptr_wrapper": {
"valid": 1,
"data": {
"value0": {
"polymorphic_id": 2147483649,
"polymorphic_name": "B",
"ptr_wrapper": {
"valid": 1,
"data": {
"value0": {}
}
}
}
}
}
}
}
The output using develop:
Saving Container (A)
Obj Addr: 0x1f74e18
Obj: 0x1f74e20 0x1f74e24 0x1f74e28
Saving B Obj: 0x1f74e10
This: 0x1f74e20 0x1f74e24 0x1f74e28
This: 8 8 128
Saving A Obj: 0x1f74e18
This: 0x1f74e20 0x1f74e24 0x1f74e28
This: 8 8 128
{
"value0": {
"ptr_wrapper": {
"valid": 1,
"data": {
"value0": {
"polymorphic_id": 2147483649,
"polymorphic_name": "B",
"ptr_wrapper": {
"valid": 1,
"data": {
"value0": {}
}
}
}
}
}
}
}
So whatever was causing this problem is likely fixed in the current develop branch of cereal, which will be released as 1.2 in the near future.
I've got a question that should be interesting. I'd like to "forward initialize" an item in a std::unordered_map upon construction.
These are the details. I've got a hash map from std::string to a custom class prop, which in my dreams, would initialize a member variable calculating the hash of the string passed to std::unordered_map::operator[].
This is a handy code I've written, but I don't know where to start.
Why this trouble? Because I'd like to avoid something like "if the string is NOT in the container calculate the hash; do stuff with prop". Avoiding this if could be something that might affect my performances. So the constructor, as well as the hashing, will be executed only once, when the map adds a new item in the container. It would be great.
Any hints?
Thanks & Cheers!
#include <iostream>
#include <string>
#include <unordered_map>
class prop
{
public:
prop(std::string s = "") : s_(s), hash_(std::hash<std::string>()(s))
{
// Automagically forwarding the string in the unordered_map...
};
std::string s_;
std::size_t hash_;
int x;
};
int main(int argc, const char * argv[])
{
// Forward the std::string to the prop constructor... but how?
std::unordered_map<std::string, prop> map;
map["ABC"].x = 1;
map["DEF"].x = 2;
map["GHI"].x = 3;
map["GHI"].x = 9; // This should not call the constructor: the hash is there already
std::cout << map["ABC"].x << " : " << map["ABC"].s_ << " : " << map["ABC"].hash_ << std::endl;
std::cout << map["DEF"].x << " : " << map["DEF"].s_ << " : " << map["DEF"].hash_ << std::endl;
std::cout << map["GHI"].x << " : " << map["GHI"].s_ << " : " << map["GHI"].hash_ << std::endl;
std::cout << map["XXX"].x << " : " << map["XXX"].s_ << " : " << map["XXX"].hash_ << std::endl;
return 0;
}
Just use your prop class as a key, instead of string:
#include <iostream>
#include <string>
#include <unordered_map>
class prop
{
public:
prop(std::string s = "") : s_(s), hash_(std::hash<std::string>()(s))
{
// Automagically forwarding the string in the unordered_map...
};
std::string s_;
std::size_t hash_;
};
int main(int argc, const char * argv[])
{
// Forward the std::string to the prop constructor... but how?
std::unordered_map<prop, int, ...> map( ... );
prop pABC( "ABC" ), pDEF( "DEF" ), pGHI( "GHI" );
map[pABC] = 1;
map[pDEF] = 2;
map[pGHI] = 3;
map[pGHI] = 9;
std::cout << map[pABC] << " : " << pABC.s_ << " : " << pABC.hash_ << std::endl;
std::cout << map[pDEF] << " : " << pDEF.s_ << " : " << pDEF.hash_ << std::endl;
std::cout << map[pGHI] << " : " << pGHI.s_ << " : " << pGHI.hash_ << std::endl;
prop pXXX( "XXX" );
std::cout << map[pXXX] << " : " << pXXX.s_ << " : " << pXXX.hash_ << std::endl;
return 0;
}
I omitted custom hash and compare function, the idea should be clear without it.