Static array initialization... with const pointers... to overloaded, templatized member functions. Is there a way it can be done (C++03 standard code)? I mean, if I have the template class
template <class T1, class U1, typename R1>
class Some_class {
public:
typedef T1 T;
typedef U1 U;
typedef R1 R;
R operator()(T& v) { /* dereference pointer to a derived class (U),
overloaded member function (U::f) */ };
private:
static R (U::* const pmfi[/* # of overloaded functions in U */])(T&);
};
Used as
template <class BASE, typename RET>
class Other_class : public Some_class<BASE, Other_class<BASE, RET>, RET> {
RET f(/* type derived from BASE */) {}
RET f(/* other type derived from BASE */) {}
RET f(/* another type derived from BASE */) {}
...
};
Question: how can I initialize de array pmfi (no typedefs, please)?
Notes:
1. As a static array MUST be initialized at file scope, template parameters and pmfi must be full qualified (the only way I know to access template parameters outside a class scope is to typedef them...).
2. So far so good. No problems with the compiler (Comeau 4.3.10.1). Problems start popping up when I try to fullfill the initializer list { ... }.
2.1. The compiler complains the template argument list is missing, no matter what I do.
2.2. I have no idea how to select the correct overloaded U::f function.
BTW, this is a kind of "jump table" generator from a boost.preprocessor list. The code I am trying to implement is of course much more complex then this one, but this is his essence.
Thanks for any help
To use BOOST_PP_ENUM in the way that you've shown, you would need a macro that takes a 'number' and yields an expression that is the address of an appropriate member of the appropriate class. I don't see a good way to do this without an explicit list unless the desired functions all have manufactured names (e.g. memfun1, memfun2, etc.). Except in the case, it's going to be easier to list the function address expressions explicitly that to used BOOST_PP_ENUM.
You are using identifiers in this array that are the same as the template parameters in Some_class.
R (U::* const pmfi[])(T&) = { /* ... */ }
Is this really supposed to be the templated member of Some_class?
template< class T, class U, class R >
R (U::* const Some_class<T, U, R>::pmfi[])(T&) = { /* ... */ }
If so, is the same instantiation going to work with all combinations of types that you are going to us the template Some_class with? If so, you have a very constrained set of classes, perhaps you can do away with the template. If not, you are going to have to specialize Some_class for every combination of template parameters in which case the template is not gaining you very much.
Edit, post edit: If I've understood you correctly then you can't do what you've suggested because the array of pointers must be of exactly the right signature.
Reducing it to a simple function pointer example, you can't do this:
void f(Derived&);
void (*p)(Base&) = &f;
otherwise, it would subvert type safety:
OtherDerived od; // derived from Base, but no from Derived
// I've managed to pass something that isn't a Derived reference to f
// without an explicit (and dangerous) cast
(*p)(od);
In your array of function pointers, the initializers must all be to functions of the right signature.
Related
EDIT: just to clarify, I understand why this code does not work,and I'm not attempting to fix it, but to understand what are the dangers if this code could compile without the semantic error.
I have found that the following code will cause a static semantic error.
I know that it's because std::list is a template class and that means that std::list<foo*> is a different type than std::list<bar*>
My question is, if the compiler would allow this type of conversion from std::list<bar*> to std::list<foo*> in the second call to print_all , what could be the danger?
I have searched around the site but couldn't find examples to bad things that could happen.
I have also tried to think of such examples but I'm not sure that these are correct or accurate.
(for example if the compiler would allow this, could I add a foo* object into the bar list in print_all() because it was converted to list?).
#include <list>
using std::list;
class foo {
// ...
};
class bar : public foo {
// ...
};
static void print_all(list<foo*>& L) {
// ...
}
list<foo*> LF;
list<bar*> LB;
// ...
print_all(LF); // works fine
print_all(LB); // static semantic error
list<foo*> and list<bar*> are two completely different classes, with no connection between them, other than beeing generated from the same template std::list (template<class T, class Allocator = std::allocator<T>> class list;). As such there isn't a conversion between them, unless the author of the template std::list explicitly wrote a conversion between std::list<T, A> and std::list<U, A> when T is a base class of U. And that is not written.
One way to achieve what you want is by creating template function:
template <class T, class A>
void print_all(std::list<T*, A>& l)
Now, there are a few caveats to be aware of:
I see no reason why you would make that static member. I would make it free function, but I would put it in a namespace with foo and bar
If you want to restrict it's usage strictly to foo and its derived classes you can do it with sfinae:
template <class T, class A, class E = std::enable_if_t<std::is_base_of_v<foo, T>>>
auto print_all(std::list<T*, A>& l)
{
}
And finally you should consider turning this into idiomatic C++ way of printing, aka streams and add the operator<<(std::ostream&, const std::list<T*, A>&) along with operator<<(std::ostream, const foo&) and maybe a virtual print function on foo. Here is extra important to define them in the same namespace as foo and bar.
You cannot use one vector instead of other because are different classes, but you can convert one vector to other implicitely or explicitely, using transformation or copy/ move constructors and assignment operator. It can cost you additional performance ( and memory in case of copy) overhead, but can be a deal in your particular case.
Is it possible to make an array of a struct with template type fields?
template<typename T>
struct MyStruct {
T *pField;
};
MyStruct< ?? > mystruct_arr[] = {
{ pFieldOfType1 },
{ pFieldOfType2 },
};
The above obviously doesn't work, but is it possible with other techniques?
I am trying to loop through the array mystruct_arr and call this function on every struct row:
template<typename T>
void SetupField(T &pSourceField, ...)
{
Base *field = ...->findBaseFieldFromDatabase(...);
...
pSourceField = static_cast<T>(field);
...
}
The reason is to try to refactor a very repetitive piece of code where I have to static_cast a long list of different types according to a few different arguments, without making it overly complicated.
A template is not a class, or a struct. It can be thought of as a blueprint, or a recipe, for a class or a struct that gets created when the template gets instantiated.
A template becomes an actual class or a struct only when it is instantiated, by specifying the required template parameters:
MyStruct<int>
Now you have an actual, living, breathing class here. But a MyStruct<int> would be a completely different class than MyStruct<char>. Without specifying the template parameters, MyStruct is not a class, or a struct, or anything that takes up a single byte of RAM. It's just a template for some struct or class.
But with template parameters, such as MySutrct<int>, this becomes an actual class with fields, and perhaps methods. Now that you have a class you can certainly have an array of these, now:
MyStruct<int> mystruct_arr[] = {
};
Or you could have a different array of MyStruct<char>s:
MyStruct<char> mystruct_arr2[] = {
};
But you can't have an array that contains both of these for the same, exact, precise reason that you can't have a single array containing a smorgasbord of different types and classes. You can't have an array that contains both chars, ints, floats, pointers, or various classes. An array always contains values of the same type/class. So pick one particular MyStruct<whatever>, and make an array out of it, that's all you can do.
But you can also declare another structure, now:
struct many_structs {
MyStruct<int> int_struct;
MyStruct<char> char_struct;
// ...
};
This sort of starts to look like an array you would like to have. But it's not an array. It's just an ordinary struct; and instead of using an array index to access a particular template instance, you would refer to the struct member directly.
You could, with some additional work, specialize std::get for your structure, and make this structure look like an array. But now you just realized that you reinvented std::tuple, and can simply to do this:
std::tuple<MyStruct<int>, MyStruct<char>> mystruct_tuple;
Finally, the closest approximation to what you're trying to do is possible only with some additional work, and with a C++ compiler that supports C++17, by declaring an array containing std::anys, or perhaps std::variants. If the array should only contain an a limited enumeration of template instances, std::variant gives the most type-safety, and convenience:
std::variant<MyStruct<int>, MyStruct<char>> mystruct_arr[]={
};
The resulting array contains only these two particular template instances. And with std::any, the screws get loosened further but you'll have to do more work to use and access each value in the array.
You think you want an array of templates.
What you really want is an array of types you can call a particular template function on.
The first is not possible. The second is called type erasure in C++.
template<class T>
using setup_fptr=void(*)(T &, Foo)
using gen_setup_ptr=void(*)(void*, Foo);
template<class T>
setup_ptr<T> get_setup(){ return SetupField<T>; }
template<class T>
gen_setup_ptr get_gen_setup(){
return [](void* p, Foo f){ get_setup<T>( *static_cast<T*>(p), f ); };
}
struct can_setup {
void* ptr=0;
gen_setup_ptr f=0;
can_setup(can_setup const&)=default;
can_setup& operator=(can_setup const&)=default;
can_setup()=default;
explicit operator bool() const{return f;}
template<class T>
can_setup(T* pt):
ptr(pt),
f( get_gen_setup<T>() )
{}
void setup( Foo foo ) const {
f(ptr, foo );
}
};
store an array of can_setup. Loop over them calling .setup(foo).
Foo is a placeholder for whatever other args you are using.
This technique is known as type erasure; we forget (erase) everything about T except that we can setup it.
I have implemented a pure C++11 Any class (based on this code) which is similar to boost::any and works nicely if used directly.
However I need to use this now as template parameter to assign parameters to variables. Look at this:
class A {
IRecognizer<Any, Any> *_recognizer;
template <typename T1, typename T2>
A(IRecognizer<T1, T2> *x) : _recognizer(x) {
}
}
and
template<typename Symbol, typename ATNInterpreter>
class IRecognizer {
public:
virtual int getState() = 0;
};
Even though I could assign each template parameter to an Any variable, I'm not allowed to assign IRecognizer<T1, T2> to IRecognizer<Any, Any>. Is there a solution for this problem? The error message is:
Cannot initialize a member subobject of type 'IRecognizer<Any, Any> *'
with an lvalue of type 'IRecognizer<Token *, ParserATNSimulator *> *'
The reason I use the Any class is to have a common type that could be assigned with any class reference (similar to Java's Object, but C++ has no common object type from which all other classes derive).
Maybe a different approach is possible here? I'm open for suggestions.
You are trying to convert a IRecognizer<T1, T2> * to a IRecognizer<Any, Any> *. The only allowed pointer conversion is from a derived class pointer to a base class pointer (i.e. Derived* to Base*) or to a more cv-qualified pointer to the same type (i.e. Derived* to Derived const*) or both.
IRecognizer<T1,T2> is not in the same class hierarchy as IRecognizer<Any, Any>. They are two unrelated types. You can add a constructor to convert an IRecognizer<A,B> to an IRecognizer<C,D> if that makes sense, but you can't add a similar thing for pointers.
A pointer to X is not the same as an X.
While you can convert an X into a Y (a IRecognizer<T1,T2> into an IRecognizer<Any,Any>), you cannot convert a pointer to X into a pointer to Y.
You may not want a pointer here. Instead, you might want a value.
However, an interface is not a value.
Java generics are not like C++ templates. A Java generic Bob<Type> is a wrapper around Bob<Object> -- it is actually storing an Object, with a bunch of wrapping casts to-and-from in a layer on top of it.
You can write such wrappers in C++. As an example:
class IRecognizer_base {
public:
virtual int getState() = 0;
};
template<typename Symbol, typename ATNInterpreter>
class IRecognizer:public IRecognizer_base {
};
Now, suppose getState() returned a Symbol:
class IRecognizer_base {
public:
virtual Any getState_() = 0;
};
template<typename Symbol, typename ATNInterpreter>
class IRecognizer:public IRecognizer_base {
public:
inline Symbol getState() {
return getState_(); // whatever conversion required to go from `Any` to `Symbol`
}
};
here, we expose the fact that our objects actually return an Any, but in the interface we cast them over.
If you actually exposed what operations are actually different based on the template types, you can do type erasing tactics that let you store anything that can do those operations instead of a pointer to a specific type.
I just found the following code in Qt and I'm a bit confused what's happening here.
Especially as to what reinterpret_cast<T>(0) does?
template <class T>
inline T qobject_cast(const QObject *object)
{
// this will cause a compilation error if T is not const
register T ptr = static_cast<T>(object);
Q_UNUSED(ptr);
#if !defined(QT_NO_MEMBER_TEMPLATES) && !defined(QT_NO_QOBJECT_CHECK)
reinterpret_cast<T>(0)->qt_check_for_QOBJECT_macro(*reinterpret_cast<T>(const_cast<QObject *>(object)));
#endif
return static_cast<T>(const_cast<QObject *>(reinterpret_cast<T>(0)->staticMetaObject.cast(const_cast<QObject *>(object))));
}
Anyone care to explain?
This is a little complicated...
Remember that qobject_cast<T>(obj) is a way to dynamically cast a QObject to the target type T which also derives from QObject. Now, for this to work, the macro Q_OBJECT should be included in the definition of class T.
Apparently, the qt_check_for_QOBJECT_macro call is for checking that the class really contains the Q_OBJECT macro. When the macro is expanded, it contains the following definitions:
template <typename T> inline void qt_check_for_QOBJECT_macro(const T &_q_argument) const
{ int i = qYouForgotTheQ_OBJECT_Macro(this, &_q_argument); i = i; }
template <typename T1, typename T2>
inline int qYouForgotTheQ_OBJECT_Macro(T, T) { return 0; }
So if you have an object x of type T and an object y of type U, the call x->qt_check_for_QOBJECT_macro(y) calls the function qYouForgotTheQ_OBJECT_Macro with parameters of types T* and U*. Because the function is templated with a single type parameter, the types T and U must be the same.
Now, if you call x->qt_check_for_QOBJECT_macro(x) then you should expect the types to be the same and for the compilation to trivially succeed. However, remember that this has the same type as the class the method was defined in. So if x is of a class that was derived from T but doesn't contain its own definition of qt_check_for_QOBJECT_macro, the call will fail.
So we have a way to check if the target type T contains the correct mechanism for the dynamic cast, but we don't have a object of type T to call this method on yet. That's what the reinterpret_cast<T>(0) is for. We don't need an actual object as this, since the compiler only needs the object types for the check to succeed. Instead, we call a method on a null pointer of type T.
I don't think this is allowed by the C++ standard, but it works since this isn't actually used inside the method.
I have trouble compiling a class, which has function pointers as member variables. The pointers are to functions which take an instance of a class as argument.
Like
template<class T, int N>
double (*f)(Vector<T,N> v);
I get "error: data member 'f' cannot be a member template" Compiler is gcc 4.2.
Edit
Before using templates I just had
double (*f)(Vector v);
This also works
double (*f)(Vector<double,2> v)
But I would like to have a function pointer for a function which takes a generic Vector as argument..
Use a member typedef:
template <typename T, int N>
class Vector
{
public:
/// type of function pointer
typedef double (*FuncPtr)( const Vector& );
};
// concrete type
typedef Vector<double,10> VecDouble10;
// actual function
double func( const VecDouble10>& );
// usage
VecDouble10::FuncPtr fp = func;
If you want to have a "template pointer", you could try a function object. The example below adds 1.0 to the wrapped function template.
struct AcceptsVector {
template<typename T, int N>
double operator()(Vector<T,N> v) const { return 1.0 + real_f(v); }
};
AcceptsVector f;
The difference to a "real" template pointer is that you cannot re-seat "AcceptsVector" to call another template, like you can do with normal function pointers. The binding is hardcoded at compile-time. However you can pass along f like a function pointer, and can call f with any Vector<T, N> like a template.
That isn't quite valid c++, basically what you're looking for are template-template parameters.
http://www.progdoc.de/papers/ttp/psi-ttp/psi-ttp.html
explains all about them
Generally, if you get yourself into this situation, you want to find a workaroumd, because your code becomes illegible VERY quickly
Well, the compiler doesn't know at the compile time, how many f's are you going to have (one for each possible T and N?). Therefore the compiler cannot calculate how much memory do objects of your class need. That's why such constructs are prohibited.
You can't have a templated pointer to function, that makes no sense.
But what you can do is
#include <vector>
template <typename T>
void foo(const std::vector<T>& v) {
// do something
}
void (*ptr_foo)(const std::vector<int>&) = &foo<int>;
(here the function pointers a templated function, which template argument is explicitly set to int)
Because your code makes just as much sense as:
struct X
{
template < typename T >
std::vector<T> vect;
};
You're trying to make a member variable a template. This is just not a valid C++ construct and I seriously doubt it ever will be.
How do you do what you actually want? I'm not sure since I don't know what you actually are trying to accomplish and why.