Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

static assert that template typename T is NOT complete? [duplicate]

Is there a way to static_assert that a type T is Not complete at that point in a header? The idea is to have a compile error if someone adds #includes down the road in places they should not be.

related: How to write `is_complete` template?

Using that link's answer,

namespace
{
template<class T, int discriminator>
struct is_complete {
  static T & getT();
  static char (& pass(T))[2];
  static char pass(...);
  static const bool value = sizeof(pass(getT()))==2;
};
}
#define IS_COMPLETE(X) is_complete<X,__COUNTER__>::value
class GType;
static_assert(!IS_COMPLETE(GType),"no cheating!");

unfortunately this gives "invalid use of incomlete type" error, d'oh. Is there a way to assert on the negation?

like image 290
peter karasev Avatar asked Sep 11 '14 20:09

peter karasev


2 Answers

Here is a function using expression SFINAE based on chris proposal which allows checking whether a type is complete yet.
My adoption needs no includes, errors-out when the required argument is missing (hiding the argument was not possible) and is suitable for C++11 onward.

template<typename T>
constexpr auto is_complete(int=0) -> decltype(!sizeof(T)) {
    return true;   
}

template<typename T>
constexpr bool is_complete(...) {return false;}

And a test-suite:

struct S;

bool xyz() {return is_complete<S>(0);}

struct S{};

#include <iostream>
int main() {
    std::cout << is_complete<int>(0) << '\n';
    std::cout << xyz() << '\n';
    std::cout << is_complete<S>(0);
}

Output:

1
0
1

See live on coliru

like image 140
Deduplicator Avatar answered Oct 07 '22 22:10

Deduplicator


Passing a reference through ... doesn't work.

5.2.2/7:

When there is no parameter for a given argument, the argument is passed in such a way that the receiving function can obtain the value of the argument by invoking va_arg (18.10). [note skipped — n.m.] The lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed on the argument expression. An argument that has (possibly cv-qualified) type std::nullptr_t is converted to type void* (4.10). After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the program is ill-formed.

Here's a kind-of-working solution hastily adapted from @chris's comment:

#include <iostream>
#include <utility>
namespace
{

    template<typename T, int>
        constexpr auto is_complete(int) -> decltype(sizeof(T),bool{}) {
            return true;
        }

    template<typename T, int>
        constexpr auto is_complete(...) -> bool {
            return false;
        }
}


#define IS_COMPLETE(T) is_complete<T,__LINE__>(0) // or use __COUNTER__ if supported

struct S;

static_assert(IS_COMPLETE(int), "oops 1!");
static_assert(!IS_COMPLETE(S), "oops 2!");

struct S {};

static_assert(IS_COMPLETE(S), "oops 3!");
like image 21
2 revs Avatar answered Oct 08 '22 00:10

2 revs