Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does it mean if Visual studio 2012 throws a compile error that shouldn't exist for VS2012?

When I am compiling some class which uses boost serialization, I get compile error C2996, see below for the message itself. If I lookup this error, it seems that this error was thrown only by much older versions of the compiler. How can this be?

The error message:

E:\Lib\boost_1_54_0\boost/serialization/split_member.hpp(42): error C2996: 'boost::hash_combine' : recursive function template definition

VS2013, VS2012 and also VS2012 Update 4 showed this behaviour.

like image 315
ikku100 Avatar asked Jan 15 '14 14:01

ikku100


People also ask

How do you fix a compilation error?

If the brackets don't all match up, the result is a compile time error. The fix to this compile error is to add a leading round bracket after the println to make the error go away: int x = 10; System.

What will cause compilation error?

A compile error happens when the compiler reports something wrong with your program, and does not produce a machine-language translation. You will get compile errors.


1 Answers

I had a similar problem. This seems to me like a bug in VS2012 (and above). I have been able to reproduce this in a simple .cpp file, so I opened a connect ticket.

I believe this error is erroneously reported when all of the following conditions apply:

  1. An object is deserialized using Boost Serialization somewhere in the *.cpp file.
  2. The serialized object is "deep enough" (~5 levels - i.e. A contains a vector of B, B contains a vector of C, etc. until E).
  3. Somewhere in the same *.cpp file (not necessarily in the same function), some templated function is called.

The error is no longer reported if the object is changed to be more "shallow", or if the call to the templated function is removed.

Also, this error does not reproduce in VS2010. I tested Boost versions 1.49, 1.52, 1.55.

Possible workaround: In one case, the error only appeared when we used boost::serialization::object_serializable (rather than the default object_class_info). Changing back to object_class_info may therefore be a workaround, except it will break backwards compatibility with previously serialized data.

Possible workaround #2: hide the templated function call in a separate translation unit.

The following code reproduces the error:

/*
Download Boost from boost.org and extract into "boost_1_55_0".
Open VS2010 command prompt and build using the following command. It will build sucessfully.
cl BugC2996.cpp /c /EHsc /MD /I"boost_1_55_0"

Open VS2012 command prompt and build using the same command. It will fail with error C2996 on the line that calls UnrelatedTemplateFunc.
If you add EITHER of the following definitions to the command line, it will build successfully on VS2012:
/D "DONT_CALL_FUNC"
/D "SER_CLASS=B"

So it has something to do with the depth of the serialization, plus calling an unrelated templated function.

Please only compile, don't try to link - it will naturally fail. 
*/

#pragma warning(disable:4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data (problem in Boost, unrelated to the bug in question)

#include <fstream>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/vector.hpp> 
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/shared_ptr_132.hpp>
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/utility.hpp>

struct E
{
    int x;
};

struct D
{
    std::vector<E> children;
};

struct C
{
    std::vector<D> children;
};

struct B
{
    std::vector<C> children;
};

struct A
{
    std::vector<B> children;
};


template<class Archive>
void serialize(Archive & ar, A& obj, const unsigned int /*file_version*/)
{
    ar & BOOST_SERIALIZATION_NVP(obj.children);
}

template<class Archive>
void serialize(Archive & ar, B& obj, const unsigned int /*file_version*/)
{
    ar & BOOST_SERIALIZATION_NVP(obj.children);
}

template<class Archive>
void serialize(Archive & ar, C& obj, const unsigned int /*file_version*/)
{
    ar & BOOST_SERIALIZATION_NVP(obj.children);
}

template<class Archive>
void serialize(Archive & ar, D& obj, const unsigned int /*file_version*/)
{
    ar & BOOST_SERIALIZATION_NVP(obj.children);
}

template<class Archive>
void serialize(Archive & ar, E& obj, const unsigned int /*file_version*/)
{
    ar & BOOST_SERIALIZATION_NVP(obj.x);
}



template<class T>
boost::shared_ptr<T> UnrelatedTemplateFunc(const std::string & argument);


#ifndef SER_CLASS
#define SER_CLASS A
#endif

// serialize A and the build will fail (if UnrelatedTemplateFunc is called below)
// serialize B instead of A and the build will succeed!
void Func(boost::shared_ptr<SER_CLASS> obj) 
{
    std::ifstream ifs;
    boost::archive::binary_iarchive ia(ifs);
    ia >> obj;
}

void OtherFunc()
{
    // comment this line and the build will succeed, whatever struct you decide to serialize above
    #ifndef DONT_CALL_FUNC
    UnrelatedTemplateFunc<unsigned char>("");
    #endif
}
like image 184
Yodan Tauber Avatar answered Sep 21 '22 05:09

Yodan Tauber