简体   繁体   中英

Visual Studio 2013: linker errors with custom enumerations?

I'm using the following code, written by James McNellis, to simplify declaring more-functional C++2011 enums:

#pragma once
#include <stdexcept>
#include <boost/preprocessor.hpp>
#include <vector>

// Internal helper to provide partial specialization for checked_enum_cast
template <typename Target, typename Source>
struct checked_enum_cast_impl;

// Exception thrown by checked_enum_cast on cast failure
struct invalid_enum_cast : std::out_of_range
{
    invalid_enum_cast(const char* s)
        : std::out_of_range(s) { }
};

// Checked cast function
template <typename Target, typename Source>
Target checked_enum_cast(Source s)
{
    return checked_enum_cast_impl<Target, Source>::do_cast(s);
}

// Internal helper to help declare case labels in the checked cast function
#define X_DEFINE_SAFE_CAST_CASE(r, data, elem) case elem:

// How to do this? Question asked on StackOverflow 11/30/14 -- check back again soon.
#define X_ADD_TO_TORET(r, data, elem) ToRet.push_back(elem)

// Defines an enumeration with a checked cast function.
//   name is the name of the enumeration to be defined
//   enumerators is the preprocessing sequence of enumerators to be defined.
#define DENUM(name, enumerators)                           \
enum name                                                              \
{                                                                      \
    BOOST_PP_SEQ_ENUM(enumerators)                                     \
};                                                                     \
                                                                       \
template <typename Source>                                             \
struct checked_enum_cast_impl<name, Source>                            \
{                                                                      \
    static name cast(Source s)                                      \
    {                                                                  \
        switch (s)                                                     \
        {                                                              \
        BOOST_PP_SEQ_FOR_EACH(X_DEFINE_SAFE_CAST_CASE, 0, enumerators) \
            return static_cast<name>(s);                               \
        default:                                                       \
            throw invalid_enum_cast(BOOST_PP_STRINGIZE(name));         \
        }                                                              \
        return name();                                                 \
    }                                                                  \
};                                                                  \
std::vector<name> MembersOf(name AnyItem) {     \
    return {BOOST_PP_SEQ_ENUM(enumerators)}; \
}; 

When I declare an enumeration with this (the syntax is DENUM(SYSTEM_STATES, (Loading)(Running)(Finished));, for example), MembersOf() generates a linker error whenever the file containing the declaration is linked to multiple objects -- even, for example, main.obj and someclass.obj. Specific error texts:

1>main.obj : error LNK2005: "class std::vector<enum SYSTEM_STATES,class std::allocator<enum SYSTEM_STATES> > __cdecl MembersOf(enum SYSTEM_STATES)" (?MembersOf@@YA?AV?$vector@W4SYSTEM_STATES@@V?$allocator@W4SYSTEM_STATES@@@std@@@std@@W4SYSTEM_STATES@@@Z) already defined in Controller.obj
1>UserInputProcessor.obj : error LNK2005: "class std::vector<enum SYSTEM_STATES,class std::allocator<enum SYSTEM_STATES> > __cdecl MembersOf(enum SYSTEM_STATES)" (?MembersOf@@YA?AV?$vector@W4SYSTEM_STATES@@V?$allocator@W4SYSTEM_STATES@@@std@@@std@@W4SYSTEM_STATES@@@Z) already defined in Controller.obj
1>C:\Users\UserName\Documents\MembersOf investigation\Debug\MembersOf investigation.exe : fatal error LNK1169: one or more multiply defined symbols found

I know that this worked properly a couple of months ago, before I installed SFML on my system, but I don't think that it's the cause; I reproduced this problem in a testbed project where I'm not even linking to SFML.

Other things declared in a header that uses the above enums, and other uses of boost, work properly.

You should mark MemberOf inline . Your other option is to mark it static (as mentioned in a comment by @simon). This will link, but it will potentially create a copy of MemberOf in each compilation unit that uses that function. Declaring MemberOf inline instead should force the emitted functions to be merged into one by the linker.

The reason I say should is because I believe that this behavior is implementation-defined. In practice, I believe the above is true for all major implementations, including MSVC.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM