Reputation: 1266
In a library that I'm writing I have a class:
template<typename T>
class my_class {};
and a function:
template<typename T>
void my_function(...) {};
I want to guarantee that a user of my code has called my_function<T>
somewhere in their code if they try to instantiate my_class<T>
. In other words, I want to generate a compile error if they try to instantiate the my_class
template without also instantiating the corresponding my_function
template.
Example:
int main() {
my_func<int>() // cause instantiation of my_func<int>
my_class<int> foo; // okay, because my_func<int> exists
my_class<char> bar; // compile error! my_func<char> does not exist
}
Note that my_class<T>
does not need to use my_func<T>
internally so instantiating it won't automatically cause my_func<T>
to become instantiated.
Edit[0]: I can't call my_func<T>
myself because I need the user to call it and pass in information that tells the library how to handle my_class<T>
. ie. If they want to use a my_class<char>
they need to explicitly say so in a particular place in the code. I could make my_class<T>
constructor test whether my_func<T>
has been called generate a run-time error if it hasn't but I'd prefer to be able to generate a compile-time error since it's knowable at compile-time whether my_func<T>
is ever used.
Edit[1]: One less-than-ideal way to do it would be to make a MY_FUNC
macro that creates a template specialisation of an otherwise incomplete template class...
template<typename T>
class incomplete;
#define MY_FUNC(T) template<> incomplete<T> { static const bool x = my_func<T>(); };
template<typename T>
class my_class {
template<size_t i>
class empty {};
typedef empty<sizeof(incomplete<T>)> break_everything;
}
Now the user can only use a my_class<T>
if MY_FUNC(T)
is used somewhere. However I'd prefer a solution that doesn't use macros and doesn't force the user to use MY_FUNC
at global scope rather than in a function.
Edit[2]: Thanks for the answers everyone. I realise it's impossible to guarantee at compile that the user will isn't calling my_func<T>
incorrectly. But I can make it pretty unlikely and make it generate a runtime error early on if they do.
They're supposed to define a function called, lets say, initialise_library
. The function looks like this:
void initialise_library() {
my_func<int>();
my_func<char>();
etc..
}
During the library initialisation, it checks to make sure none of the my_func
s have been called. It then calls initialise_library
. It then checks to make sure all of the my_funcs
have been called. This much is doable. If the user hasn't defined initialise_library
they get a linker error. If they haven't called my_func<T>
somewhere in their program for a my_class<T>
that they use then (idealy) they get a compile error. If they've defined initialise_library
and they've used my_func<T>
somewhere but they've used it in the wrong place they get a runtime error when their program starts up.
Basically I want to stop them from adding a my_class<T>
somewhere in their code and forgetting to put my_func<T>()
in initialise_library
, because that's something they're likely to do. The only way to runtime check this would be during the construction of my_class<T>
. This will be an annoyingly late time to make this check if they only use my_class<T>
in a deep and rarely-used chunk of code.
Upvotes: 1
Views: 1078
Reputation: 18572
There are a number of things that make this very difficult to do, almost certainly impossible.
First, you have to understand that there is a difference between what can be known at compile-time, what is actually known at compile-time by a specific compiler, what information the standard requires that a compiler should track, and what information you can actually extract and use at compile-time (and in what context you can use that information). I think that what you are asking is theoretically possible (under some assumptions, for instance, that everything happens in a single translation-unit). But I am almost certain the standard does not require that a compiler be able to give you that kind of information, let alone be required to know that kind of information itself. And I also doubt that most compilers would be able to know that information in all but the most trivial cases.
The reason for this is simple. Your question really boils down to asking the compiler if function A has been called anywhere prior to point B. Where function A is your function template and point B is the point of instantiation and first creation of your class template. This means that when the compiler reaches point B it must have analyzed all the code (and instantiated any templates) that would correspond to possible execution points prior to point B. In other words, it would require that the compiler must follow the path of execution throughout when compiling. Compilers don't do this. The function template could easily be buried in some other function that the compiler will not get to compiling before reaching point B even though that function will be executed before creating the class-template object. And the converse could be true too, i.e., the compiler has instantiated the function template, even though it is called after the class-template appears.
Then, there is the problem of branches. Instantiating a function template does not mean that it will be executed, due to some conditional statement or an exception, or even a go-to for that matter. The analysis done by the compiler would have to be pretty insane in order to tell you with certainty whether the function will have been executed for sure by the time you reach the constructor of the class template.
Then, there is the problem of translation units. What if the function template instantiation and the class template instantiation do not appear in the same translation unit? Even if you had a working mechanism to detect that the function template was instantiated, it wouldn't work in this case even if the execution order was correct.
Long story short, use a run-time check, or find a way to call the function from the constructor (maybe with default parameters and a debug warning message at run-time).
EDIT: Generally, the way to ensure, at compile-time, that a certain order of function-calls / constructor-calls is to tie them to a chain of initializations. A simple trick is to make the class template only constructible through calling a member function of another class (e.g., on a global / singleton object of that class), and make that object only constructible through calling the function template with the required parameters. There are a number of similar tricks, often involving static local variables as "initialize once" variables. But of course, these make the code a bit more weird.
Upvotes: 1
Reputation: 8687
In light of your edit, you could try something like this:
#include <iostream>
#include <stdexcept>
using namespace std;
template <typename T>
class my_class
{
public:
static bool s_isInitialized;
my_class()
{
if(!s_isInitialized) {
throw logic_error("must call my_function<T> before instantiating my_class<T>");
}
cout << "ctor ok" << endl;
}
};
template <typename T>
bool my_class<T>::s_isInitialized = false;
template <typename T>
void my_function(const char * type)
{
cout << "initializing " << type << endl;
my_class<T>::s_isInitialized = true;
}
int main()
{
my_function<bool>("bool");
my_class<bool> a;
my_function<int>("int");
my_class<int> b;
my_class<int> c;
cout << "now for something different" << endl;
my_class<short> d; // ctor throws
// this code isn't reached
}
Basically, have a flag for each template instantiation of my_class<T>
which determines if the appropriate my_function<T>
has been called. Initialize the flag to false
, and set it to true
in my_function<T>
. The constructor(s) of my_class<T>
then have to check the flag, and throw an exception if it's still false.
Although the check is at runtime rather than compile-time like you wanted, the overhead is negligent. It is a single check of a boolean, which is one instruction to load the byte and one instruction for the branch. You won't pay any overhead for the branch since any half decent hardware branch predictor will always predict correctly for every instantiation of your classes (except maybe the first instantiation or two).
Additionally, you can also add a check in my_function<T>
to check that the flag hasn't already been set, and either do nothing or throw another logic_error
(as you deem appropriate) if initialization has already occurred.
If you are using any threading/concurrency, you will of course have to protect against concurrent access to the flag/initialization function. That will require a bit more programming overhead, but it's not hard.
Upvotes: 0
Reputation: 119877
Even if it were possible to detect the presence of a call to your function at compile time, it is utterly impossible to verify that the call is in fact made at runtime, and that it happens before some other call. So you must use a run-time check anyway.
Upvotes: 1