I'm pushing IMO the limits of C++template programming. The system is an Arduino but my attempt is applicable to any microcontroller system.
I define Pins using a template class with an 'int' parameters
template<const int pin>
struct Pin {
Pin() { mode(pin, 0); }
};
template<const int pin>
class PinOut : public Pin<pin> {};
I can create template classes to use PinOut like:
template<typename F>
class M {
public:
M() { }
F mF;
};
M<PinOut<1>> m1;
template<int F>
class N {
public:
N() { }
Pin<F> mF;
};
N<1> n1;
But I'd like to not use templates in the classes that use PinOut. This is illustrative of my thinking showing possible approaches but clearly doesn't work.
class R {
public:
R(const int i) {
}
PinOut<i> mF; // create template instance here
};
R r1(1); // what I'd like to able to do
I recognize the problem is creating a type inside class R.
The other possibility is instantiating a PinOut variable and passing it in but again passing and creating a type inside the class is a problem. Something like this:
class S {
public:
S(PinOut<int>& p) { } // how to pass the type and instance
PinOut<p>& mF; // and use it here
};
PinOut<1> pp;
S s1(pp);
Sorry if this sound abrupt but please don't ask why or what I'm trying to do. This is an experiment and I'm pushing my understanding of C++ especially templates. I know there are other approaches.
Yes, any function that takes that type must itself be a template.
But is the entire family of Pin related in a way that some thing are meaningful without knowing T? This can be handled with a base class that's a non-template. The base class idea is especially handy because it can contain virtual functions that do know about T. This lets you switch between compile-time and run-time polymorphism on the fly as desired. Taken to an extreme, that becomes the weaker idea with the same syntax of "Generics" as seen in Java and .NET.
More generally, this is a concept known as type erasure. You might search for that term to find out more. It is designed into libraries in order to keep common code common and prevent gratuitous multiplication of the same passage though multiple instantiations.
In your case, pin
is a non-type argument, which is something Generics don't even do. But it may not really affect the type much at all: what about the members change depending on pin
? This might be an array bound, or a compile-time constant used to provide compile-time knowledge and optimization, or there for the sole purpose of making the type distinct.
All of these cases are things can be dealt with at run-time, too. If it's for the sole purpose of making the type distinct (e.g. make the compiler check that you pass time values and distance values to the correct parameters) then the real guts are all in a base class that omits the distinctiveness.
If it's an array bound or other type difference that can be managed at run-time, then again the base class or an adapter/proxy can do it at run-time. More generally a compile-time constant that doesn't affect the class layout can be known at run-time with the same effect, just less optimization.
From your example, that it is sensible to make the pin a constructor argument, the class could be implemented in the normal way with run-time configuration. Why is it a template? Presumably for compile-time checking to keep separate things separate. That doesn't cause them to work in different ways, so you want that compile-time part to be optional. So, this is a case where a base class does the trick:
class AnyPin
{
public:
AnyPin (int pin); // run-time configuration
};
template <int pin>
class Pin : public AnyPin { ⋯ };
Now you can write functions that take AnyPin, or write functions that take Pin<5> and get compile-time checking.
So just what does pin
do to the class, in terms of its layout and functionality? Does it do anything that makes it unacceptable to just implement it as a run-time constructor value?
You ask that we don't inquire as to what you're trying to do, but I must say that templates have certain features and benefits, and there must be some reason for making it a template. Speaking simply in language-centric terms, did I miss something with the above analysis? Can you give a C++-programming reason for wanting it to be a template, if my summary didn't cover it? That may be why you didn't get any answers thus far.