Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[READY] Add explicit instantiation of string_id<T>::NULL_ID to make clang happy. #20946

Merged
merged 1 commit into from
May 17, 2017

Conversation

kevingranade
Copy link
Member

@Leland
Copy link
Contributor

Leland commented May 5, 2017

Awesome. Does this fix #17197 as well?

@kevingranade
Copy link
Member Author

kevingranade commented May 5, 2017 via email

@kevingranade
Copy link
Member Author

After a lot more fiddling around, it turns out that

  1. The warning is legit, we were doing some really sketchy things here.
  2. It looks like in at least some cases we are relying on this behavior.

It looks like the intent is for the initialization of NULL_ID to be optional in order to allow individual specializations of the template to provide different strings. This arrangement was allowed because string_id::is_null() and string:id::string_id( const null_id_type & ) weren't often used, and the linker would discover if they were and throw an error.

New versions of clang have started warning about this scenario regardless of whether the member is used or not, which I can't argue with. Also it seems like there are some visibility problems, the member needs to be initialized in every compilation unit where it is used, which probably means the initilization needs to happen in header files instead of in cpp files as is happening now.

Two nice-to-haves:

  1. A short initialization statement, the current one is rather verbose. (template <> const emit_id string_id::NULL_ID( "null" );)
  2. A way to convince GCC and earlier versions of clang that this initialization is mandatory, if not we'll have plenty of instances of this breaking in the future.

@kevingranade kevingranade changed the title Add explicit instantiation of string_id<T>::NULL_ID to make clang happy. [WIP] Add explicit instantiation of string_id<T>::NULL_ID to make clang happy. May 5, 2017
@kevingranade
Copy link
Member Author

The "simplest" thing sems to be to hoist the initialization of the respective NULL_ID instances to the header files for their classes, but this is really wordy and possibly fragile. It also prevents the use of forward declarations sometimes.

I'm wondering if I can include the null string (if any) in an explicit template instantiation statement, then we can have the NULL_ID initialization happen in the body of string_id, and possibly restrict the verbosity of the instantiation.

@Leland
Copy link
Contributor

Leland commented May 6, 2017

Know this is a WIP but can confirm that this makes Apple's LLVM happy 🙏

@kevingranade
Copy link
Member Author

kevingranade commented May 7, 2017 via email

@kevingranade kevingranade force-pushed the defined-var-template branch from 61a9551 to f98c7bb Compare May 12, 2017 03:09
@kevingranade
Copy link
Member Author

So... more context since this is turning out to be incredibly problematic to address.

in src/string_id.h we have:

template<typename T> class string_id {
    template<typename S, class = typename
             std::enable_if< std::is_convertible<S, std::string >::value>::type >
    explicit string_id( S && id, int cid = -1 ) : _id( std::forward<S>( id ) ), _cid( cid ) {}
    string_id() : _id(), _cid( -1 ) {}
    // This member is the issue.
    static const string_id<T> NULL_ID;
    private:
    std::string _id;
    mutable int _cid;
}

Then in various compilation units we have something like (this is src/harvest.cpp)

template <>
const harvest_id string_id<harvest_list>::NULL_ID( "null" );

At other points we have references to NULL_ID.

The result is the kind of warning in #18493:

src/string_id.h:66:50: error: instantiation of variable 'string_id<emit>::NULL_ID' required here, but no definition is available [-Werror,-Wundefined-var-template]
        string_id( const null_id_type & ) : _id( NULL_ID._id ), _cid( NULL_ID._cid ) {}
                                                 ^
src/emit.h:14:18: note: in instantiation of member function 'string_id<emit>::string_id' requested here
        emit() : id_( NULL_ID ) {}
                 ^
src/string_id.h:154:35: note: forward declaration of template entity is here
        static const string_id<T> NULL_ID;
                                  ^
src/string_id.h:66:50: note: add an explicit instantiation declaration to suppress this warning if 'string_id<emit>::NULL_ID' is explicitly instantiated in another translation unit
        string_id( const null_id_type & ) : _id( NULL_ID._id ), _cid( NULL_ID._cid ) {}

I've tried various things to sort this out, but so far I haven't found a way to make both clang and gcc happy. Clang throws the above warning on solutions gcc is happy with, and the gcc linker complains about multiple template definition if I add the explicit instantiation declarations Clang suggests. This is the explicit declaration I'm using:

extern template const string_id<harvest_list> string_id<harvest_list>::NULL_ID;

This is the explicit specialization:

template<> const string_id<harvest_list> string_id<harvest_list>::NULL_ID = string_id<harvest_list>( "null", 0 );

@kevingranade kevingranade force-pushed the defined-var-template branch from f98c7bb to fe06466 Compare May 14, 2017 05:48
@kevingranade kevingranade changed the title [WIP] Add explicit instantiation of string_id<T>::NULL_ID to make clang happy. [READY] Add explicit instantiation of string_id<T>::NULL_ID to make clang happy. May 14, 2017
@kevingranade
Copy link
Member Author

Wow, just wow. This turned into a massive pain in the ass, but I finally have it sorted out.
Some combination of explicit declaration of templates, removing the code that created a special generic NULL_ID, and isolating the NULL_ID specialization statements in their own .cpp file has sorted out warning-free compilation on both gcc and clang 4.

@kevingranade
Copy link
Member Author

I moved some code from various headers to .cpp files, it didn't end up being necessary, but there should be as little code in headers as possible, so it's better as it is.
Similarly, I split activity_type out into its own file, also in an unsuccessful attempt to break a deadlock between implicit template instantiation and specialization, but it's large enough for its own file, so it can stay.

@Leland
Copy link
Contributor

Leland commented May 15, 2017

Conflict in effect.h from #20900

@Leland
Copy link
Contributor

Leland commented May 15, 2017

Getting this error after that merge:

clang++  -DRELEASE -DMACOSX -DGIT_VERSION -DOSX_SDL2_LIBS -DTILES -DUSE_HOME_DIR -ffast-math -Os  -Wall -Wextra    -fsigned-char -Werror -stdlib=libc++ -std=c++11 -MMD -mmacosx-version-min=10.7 -D_THREAD_SAFE -I/usr/local/include/SDL2 -DSDL_SOUND -I/usr/local/include/SDL2 -D_THREAD_SAFE -I/usr/local/include -c src/mutation.cpp -o obj/tiles/mutation.o
src/mutation.cpp:568:22: error: instantiation of variable 'string_id<mutation_branch>::NULL_ID' required here, but no definition is available
      [-Werror,-Wundefined-var-template]
    return trait_id::NULL_ID;
                     ^
src/string_id.h:146:35: note: forward declaration of template entity is here
        static const string_id<T> NULL_ID;
                                  ^
src/mutation.cpp:568:22: note: add an explicit instantiation declaration to suppress this warning if 'string_id<mutation_branch>::NULL_ID' is explicitly
      instantiated in another translation unit
    return trait_id::NULL_ID;
                     ^
1 error generated.
make: *** [obj/tiles/mutation.o] Error 1

macOS 10.12.4, compiled with Homebrew, compilation command was make app NATIVE=osx OSX_MIN=10.7 RELEASE=0 TILES=1 LOCALIZE=0 CLANG=1 USE_HOME_DIR=1 SOUND=1

@kevingranade
Copy link
Member Author

Booo >_<
It needs something like

Extern template const string_id<muration_branch> string_id<mutation_branch>::NULL_ID;

probably in mutations.h

@kevingranade kevingranade force-pushed the defined-var-template branch from 3ba9942 to 00ad18e Compare May 16, 2017 03:53
@kevingranade
Copy link
Member Author

Should be sorted out now.

@Leland
Copy link
Contributor

Leland commented May 17, 2017

@kevingranade can confirm; successful build!

@kevingranade kevingranade merged commit dbf94ea into CleverRaven:master May 17, 2017
@kevingranade kevingranade deleted the defined-var-template branch November 9, 2018 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Error compiling with clang Current master (8a5215) doesn't build with clang
2 participants