1

During initial development of a template class, before I've written full test cases, I am finding that I would like the ability to force the compiler to generate the code for every member (including non-static ones) of a template class for a specific set of template parameters, just to make sure all the code at least compiles.

Specifically, I'm using GCC 9 (I don't really need this ability for other compilers, since it's just something I want to temporarily make happen during development); and its c++14, c++17, c++2a and c++20 standards.

For example, I might write the following code:

template <typename D> struct test_me {
  D value;
  void mistake1 () { value[0] = 0; }
  void mistake2 () { mistake1(value); }
  // and a bajillion other member functions...
};

And, given that I know in advance the finite set of possible template parameters (let's say int and float here), I just want to make sure they at least compile while I'm working.

Now I can do this, which obviously falls short:

int main () {
  test_me<int> i;
  test_me<float> f;
}

Since mistake1 and mistake2 aren't generated, the code compiles fine despite the indexed access attempt to a non-array type, and the incorrect function call.

So what I've been doing during development is just writing code that calls all the member functions:

template <typename D> static void testCalls () {
    test_me<D> t;
    t.mistake1();
    t.mistake2();
    // and so on... so many mistakes...
}

int main () {
   testCalls<int>();
   testCalls<float>();
}

But this gets to be a pain, especially when the member functions start to have complex side effects or preconditions, or require nontrivial parameters, or have non-public members and not-yet-developed friends. So, I'd like a way to test compilation without having to explicitly call everything (and, ideally, I'd like to be able to test compilation without modifying any "test" code at all as I add new members).

So my question is: With at least GCC 9, is there a way to force (potentially temporarily) the compiler to generate code for a template class's entire set of members, given template parameters?

Jason C
  • 38,729
  • 14
  • 126
  • 182
  • 1
    Declare a set of (member) function pointers for them, and instantiate these. – πάντα ῥεῖ Dec 29 '20 at 20:31
  • @πάνταῥεῖ That's not a bad idea. Actually I found it's even simpler after playing around with it; you don't even need to declare the pointer types, you can just take the function addresses: https://ideone.com/eq5qtV Still -- I wonder if there's a way to do it without having to maintain a corresponding list of `&members`. – Jason C Dec 29 '20 at 20:39
  • @n.'pronouns'm. This is during development of e.g. header-based template library code. – Jason C Dec 29 '20 at 20:40
  • 1
    Write a unit test for your function. If you don't have one, you should not have the function in the first place. Write it **together** with the function. – n. m. could be an AI Dec 29 '20 at 20:43
  • 1
    @JasonC Well the idea is to declare a _type trait_ class for `T`, that you can check at a certain point with number of `static_assert`s. This will make error messages better readable an you can point out what exactly is missing from `T` even if no other code tries to instantiate that funciton. – πάντα ῥεῖ Dec 29 '20 at 20:47
  • 1
    Reflection and reification will make this possible. – Yakk - Adam Nevraumont Dec 29 '20 at 20:59
  • @Yakk-AdamNevraumont !!! Is [N4766](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4766.pdf) being considered? I haven't been keeping up on my C++ news. Oh man, it's going to be great, and it's going to be nightmarishly misused too. It'll be like... Java is a country that's never had a minimum drinking age, and C++ is a country where it used to be 21 and suddenly it's legal for everyone. If that analogy makes sense. (Qt land might get weird, too.) – Jason C Dec 29 '20 at 21:03
  • 1
    @JasonC https://www.infoworld.com/article/3528882/c-plus-plus-20-spec-finalized-c-plus-plus-23-spec-begins.html – Yakk - Adam Nevraumont Dec 29 '20 at 21:28

2 Answers2

3

Just explicitly instantiate the class:

template struct test_me<int>;
template struct test_me<float>;

Demo

cigien
  • 57,834
  • 11
  • 73
  • 112
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Ah! I suspected it was something simple that I already knew how to do, but I couldn't connect the dots. Awesome, thanks. – Jason C Dec 29 '20 at 21:27
  • 1
    @JasonC Note a caveat for this solution: your member functions must defined *before* the explicit instantiation, otherwise those definitions are not guaranteed to be instantiated. [demo](https://godbolt.org/z/Goa57q). If your testing code is sure to be after all the definitions of your class members, then this solution will work, but it's something worth bearing in mind. – cigien Dec 29 '20 at 21:41
  • 1
    @cigien Another caveat is that this won't instantiate member templates (which is expected, I guess, but it's not a catch-all solution). At the end of the day I needed a combination of this answer *and* [your answer](https://stackoverflow.com/a/65498567/616460) to cover member templates. This is a rare occasion when I wish I could mark both answers as correct. I hope you didn't take the check mark switch personally; blame it on the system. :) – Jason C Dec 30 '20 at 17:10
  • 1
    You have indeed to (explicitly) instantiate members template too (`template void test_me::foo();`). – Jarod42 Dec 30 '20 at 19:24
2

What you are trying to do is not allowed by the language, at least with implicit instantiations of your test class. When you implicitly instantiate test_me with some type, the definitions of the member functions are not allowed to be implicitly instantiated, as per temp.inst#11:

An implementation shall not implicitly instantiate a function template, a variable template, a member template, a non-virtual member function, a member class or static data member of a templated class, or a substatement of a constexpr if statement ([stmt.if]), unless such instantiation is required.

So if you want to implicitly instantiate all the mistake member functions, you have no choice but to require their instantiation somehow. As in your example with testCalls, you can make calls to those member functions, or you can ODR-use them some other way, such as taking their addresses.

cigien
  • 57,834
  • 11
  • 73
  • 112
  • 1
    Darn. Couldn't be any clearer though. Thanks for pulling that up. Taking the addresses seems to be a decent option ([example](https://ideone.com/eq5qtV)). – Jason C Dec 29 '20 at 20:48
  • 1
    @JasonC Yeah, it may seem unfortunate, but there are actually very good reasons for this constraint (feature?). And yes, I think taking their addresses is a nice way of expressing that you want those member functions to be instantiated. – cigien Dec 29 '20 at 20:51