7

I'd need a template which can be called like this:

int x = type_exists< std::vector<int> >::value;

This should set x to 1 if #include <vector> was present (either explicitly or transitively) earlier in the source, otherwise it should set x to 0.

Is it possible to do it in C++? I'm using GCC, so GCC extensions are also fine.

It's also OK to change the call syntax a bit.

It's not OK to run the C++ compiler twice: first just to figure out if we get a compile error.

pts
  • 80,836
  • 20
  • 110
  • 183
  • 6
    What would you do with such a function? – Mat Dec 15 '13 at 11:07
  • 3
    Related: [How to detect existence of a class using SFINAE?](http://stackoverflow.com/questions/10711952/how-to-detect-existence-of-a-class-using-sfinae) – gx_ Dec 15 '13 at 11:08
  • @gx_: I couldn't find anything useful there. `class_defined` would require adding `CLASS_DEFINED_CHECK` to the `vector` header, and `has_destructor` is a compile error for undefined classes. – pts Dec 15 '13 at 11:13
  • @Mat: Once that's available, I'd like provide a fallback implementation for classes which are not defined in the library header. – pts Dec 15 '13 at 11:15
  • 3
    In general, you can only use names in C++ which have been declared. There is no general "reflection" or "bare word" support in the language that would allow you to, say, examine the state of the parser. It's a little different for class template members, but that's special. – Kerrek SB Dec 15 '13 at 11:17
  • @pts: what if someone includes `vector` after your header? I'm really not seeing your use-case. – Mat Dec 15 '13 at 11:18
  • @Mat: Let's suppose some libraries' `vector` headers define the `supervector` template as well. My header includes `vector`, and I'd like to use the system's `supervector` if available, otherwise, I'd like to use my fallback implementation. – pts Dec 15 '13 at 11:26
  • 1
    @pts As clearly explained for `has_destructor`, you need to _declare_ the name to test, then it works: http://coliru.stacked-crooked.com/a/4b39006dfae715d0 . A potential problem, as shown in the second part of the `main`, is that if you declare a defined class you must do it at the same scope (and namespace) of the definition. – gx_ Dec 15 '13 at 11:29
  • 1
    @gx_: How does `has_destructor` work for a template like `vector`? How do I pre-declare that? – pts Dec 15 '13 at 11:32
  • 1
    If it's Visual C++ you use `__if_exists`. If not, well, sorry... – user541686 Dec 15 '13 at 11:53
  • @pts Can you explain why you don't want to use the standard implementation of `vector` and why you don't want to check for the symbol `_VECTOR_` ? – MikeMB Dec 15 '13 at 11:54
  • 1
    @pts Indeed `vector` is a problem. If you forward-declare `namespace std { template class vector; }` you can't use “`std::vector`” (missing 2nd template argument) if `` wasn't included, but if you try `namespace std { template class allocator; template > class vector; }` then you will get an error (redefinition of default template argument) if `` _was_ included or later is. Sorry I can't find a solution at the moment. (Note that I linked the other question as “related”, not “duplicate”) – gx_ Dec 15 '13 at 12:09
  • I'm curious. What would the use case be? – Shoe Dec 15 '13 at 12:23
  • 1
    @MikeMB The C++ Standard doesn't say that the header `` defines `_VECTOR_`. With GCC 4.8.1 it doesn't; it defines `_GLIBCXX_VECTOR` as an include guard (but I wouldn't rely on that). – gx_ Dec 15 '13 at 12:24
  • @gx_ Not only that, but forward declaring a class from the standard is (in most cases) undefined behaviour. – Rapptz Dec 15 '13 at 12:31
  • 1
    @Rapptz I wasn't sure about forward declarations (as opposed to adding definitions or new declarations), but [indeed](http://stackoverflow.com/questions/307343/forward-declare-an-stl-container) (I wasn't comfortable with that anyway...). Thanks for the reminder :) – gx_ Dec 15 '13 at 12:48
  • @gx_ The [link](https://groups.google.com/forum/#!topic/comp.lang.c++.moderated/XBKFjGNquDM) in the link says `section 17.4.3.1/3: "If the program declares or defines a name in a context where it is reserved, other than as explicitly allowed by this clause, the behavior is undefined."` So I guess that's the end of it. [This link](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#94) also says `...I can't think of any way that this extension could break a conforming program, considering that users are not permitted to forward-declare standard library components...` –  Dec 15 '13 at 12:56

2 Answers2

8

This is not what you are looking for, but it's as close as you can get to a type_exists trait:

template<class T> struct Void { typedef void type; };

template<class T, class U = void>
struct type_exists { enum { value = 0 }; };

template<class T>
struct type_exists<T, typename Void<T>::type> { enum { value = 1 }; };

Apparently, it works:

static_assert(type_exists<int>::value, "int is not defined");
static_assert(type_exists<SomeNonexistingType>::value, "expected compile-time error");

This does exactly what it is supposed to do. Tested with GCC 5.4.0.

ManuelAtWork
  • 2,198
  • 29
  • 34
  • 1
    Indeed, with this definition of `type_exists`, `int x = type_exists< std::vector >::value;` doesn't compile if `` is not included, so this doesn't solve my problem. – pts Dec 01 '16 at 16:44
4

This is not possible, I'm afraid. If we were to use a non defined identifier we would get a compilation error, leading to this code:

int x = type_exists< std::vector<int> >::value;

not to even compile.

Also, the standard doesn't specify any preprocessor directive to be declared within the header file (which is implementation defined instead) for the standard library, therefore you won't be able to detect it even with preprocessor macros.

Shoe
  • 74,840
  • 36
  • 166
  • 272