3

I need to do something like this:

template <typename Matrix_xx> bool ProcessMatrix<Matrix_xx>::function1(Matrix_xx a) { int x, y; // ... some code here ... // if (Matrix_xx == Matrix_1D) { a->readFromFile(x); } else if (Matrix_xx == Matrix_2D) { a->readFromFile(x, y); } // ... } 

i.e., to call different functions depends on the template argument. The code above wouldn't compile because there are only Matrix_1D::readFromFile(int x) and Matrix_2D::readFromFile(int x, int y). I don't want to split function1 into two different functions only because there would be a lot of doubled code. Is there another way?

4
  • 2
    I guess you already know about enable_if but you'd like to have an "inline" solution. static_if is the answer to your question, and it's a highly anticipated feature for C++. Commented Oct 11, 2012 at 6:59
  • BTW, there are almost always ways to go around the absence of static_if, but from your reduced example, it's impossible to suggest anything general to you. The most general thing is static_if which is not in the language yet. Commented Oct 11, 2012 at 7:05
  • @enobayram: enable_if would be useful together with "has_method" trait to define the trait wrapping the method call for any class that has it, but the wrapper has to be defined in the first place anyway. Commented Oct 11, 2012 at 7:48
  • @Jan That's my point also, currently, there's no easy and generic way to solve this problem without introducing some unnatural code outside the flow of the function. Commented Oct 11, 2012 at 8:31

3 Answers 3

10

Wrap the type-specific code in either overloaded function or explicitly specialized template:

void doReadFromFile(Matrix_1D &a, int x, int y) { a->readFromFile(x); } void doReadFromFile(Matrix_2D &a, int x, int y) { a->readFromFile(x, y); } template <typename Matrix_xx> bool ProcessMatrix<Matrix_xx>::function1(Matrix_xx a) { int x, y; // ... some code here ... // doReadFromFile(a, x, y); } 

If Matrix_xx is Matrix_1D, overloading will select the first overload, if it is Matrix_2D, overloading will select the second overload and if it's anything else, it won't compile. But if somebody provides new type of matrix, they can make it compile by defining the doReadFromFile for it.

This is generally useful trick and reason why standard library uses "traits"—they can be defined for class somebody gives you and they can be defined for non-class types. The "traits" can be either in the form of explicitly specialized templates or free functions, usually looked up with argument-dependent lookup (placed in the namespace of their argument, not the template).

For completeness, the explicit specialization would look like:

template <typename Matrix_xx> struct doReadFromFile {}; template <> struct<Matrix_1D> struct doReadFromFile { void operator()(Matrix_1D &a, int x, int y) { a->readFromFile(x); } } template <> struct<Matrix_1D> struct doReadFromFile { void operator()(Matrix_1D &a, int x, int y) { a->readFromFile(x, y); } } 
Sign up to request clarification or add additional context in comments.

Comments

1

Couldn't you make the argument to readFromFile a reference to a vector and let the Matrix_xx instance decide how many indices to fill in? That would eliminate the need for a conditional check.

Comments

0

You can get around that using the typeid operator.

Using it, your code would become:

template <typename Matrix_xx> bool ProcessMatrix<Matrix_xx>::function1(Matrix_xx a) { int x, y; // ... some code here ... // if (typeid(Matrix_xx) == typeid(Matrix_1D)) { a->readFromFile(x); } else if (typeid(Matrix_xx) == typeid(Matrix_2D)) { a->readFromFile(x, y); } // ... } 

or you could do it with a switch:

switch(typeid(Matrix_xx)) { case typeid(Matrix_1D): a->readFromFile(x); break; case typeid(Matrix_2D): a->readFromFile(x,y); break; // etc, etc } 

[EDIT:] For those that say it won't compile, I'd be most interested in knowing what the functional difference between the code I left above and the code below, which is taken from a working project:

pdfArray.h

#ifndef PDFARRAY_H #define PDFARRAY_H #include "pdfObj.h" #include "pdfTypes.h" class pdfArray : public pdfObj { public: pdfArray(); pdfArray(const pdfArray &src); size_t size() { return(mArray->size()); } void clear(); ~pdfArray(); template <typename T> void addItem(const T *newItem) { //cout << typeid(T).name() << endl; pdfObj *item = new T(*newItem); mArray->push_back(item); } pdfObj *getItem(unsigned int itemIndex); const bstring& toString(); pdfArray &operator=(const pdfArray &src); private: vecObjPtr *mArray; }; #endif // PDFARRAY_H 

excerpt from pdfArray.cpp

pdfArray::pdfArray(const pdfArray &src) { vecObjPtrIter iter; pdfObj *ptr; mArray = new vecObjPtr; for (iter=src.mArray->begin(); iter!=src.mArray->end(); iter++) { ptr = *iter; if (typeid(*ptr) == typeid(pdfString)) addItem( (pdfString*)ptr ); if (typeid(*ptr) == typeid(pdfInt)) addItem( (pdfInt*)ptr ); if (typeid(*ptr) == typeid(pdfFloat)) addItem( (pdfFloat*)ptr ); if (typeid(*ptr) == typeid(pdfArray)) addItem( (pdfArray*)ptr ); } } 

9 Comments

Good thinking, but 1) Matrix_1D might not have readFromFile(int,int) defined. 2) it might be an overkill to use RTTI for this.
@enhzflep: Matrix_1D has readFromFile(int). Matrix_2D has readFromFile(int, int). Since you are trying to call both on both types (both branches of the if are type-checked for both instantiations by compiler even though one might later be discarded by optimizer), your code won't compile. Only way to exclude the code from compilation is to use explicitly specialized templates or overloaded functions.
The explicit check changes nothing, the entire code still need to compile! Assuming that Matrix_1D defines only readFromFile(int) and Matrix_2D defines only readFromFile(int, int), you will always get a compilation error on the branch of the if that is not taken at runtime.
Ad edit: The functioning code selects which overload to call from a set of overloads that all exist. The code in question however tries to select between overloads that don't exist for the other instantiation.
Your first code is actually similar to this one: struct Foo { void foo() {} }; void f(Foo obj) { if (true) obj.foo() else obj.bar(); }. f will obviously fail to compile because Foo does not define a bar member function. This is exactly the same as what you propose in your solution.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.