473,785 Members | 2,411 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Template Class Specialization

Hi all,

I whipped up a quick class to represent a matrix for use with LAPACK.
It's a template class so it can support the 4 data types supported by
LAPACK (single/double x complex/real). I added a conversion operator
to automatically convert the object to a pointer of the appropriate
type. This makes using LAPACK in C++ a lot easier.

Unfortunately, it does not work well for the complex data types. The
reason is that LAPACK (or, more specifically, f2c) defines its own
complex types, complex and doublecomplex. I could just use LAPACK's
types, but they don't provide any operators for them, making them a
real pain. Instead, I would much prefer to use std::complex.

This is where the problem is. The conversion operator in my matrix
class returns a T* where T is the underlying data type. For complex
data types, it needs to return a pointer to an LAPACK type,
specifically a complex* or doublecomplex*. I thought template
specialization would help me out, but it's turned out to be a real
pain.

Here's the code in question:

template<typena me T>
class LAPACK_Matrix
{
public:
operator T*() { return &m_Data[0]; }
/* other operators */

private:
std::vector<Tm_ Data;
};

So I thought I would just provide a specialized version for the complex
data types:

template<>
class LAPACK_Matrix<s td::complex<dou ble
{
public:
operator doublecomplex*( ) { return &m_Data[0]; }
};

I thought this would be sufficient, but (as I'm sure you all know), the
specialized class does not "inherit" anything from the unspecialized
class. So in order to make this work, I would have to re-implement all
my other operators in the specialized class. That doesn't seem like a
good solution to me.

So I've been coming up with a reasonable way to avoid having to rewrite
a bunch of code and I'm just stumped. I thought maybe inheritance
could solve my problem, but I don't see how. I thought about trying to
combine templates and in heritance, but I'm not sure how that would
work, either.

If anyone has any suggestions, I'd love to hear them.

Thanks in Advance,
Bill

Sep 8 '06 #1
7 2182
wo******@gmail. com wrote:
[...]
Here's the code in question:

template<typena me T>
class LAPACK_Matrix
{
public:
operator T*() { return &m_Data[0]; }
/* other operators */

private:
std::vector<Tm_ Data;
};

So I thought I would just provide a specialized version for the
complex data types:

template<>
class LAPACK_Matrix<s td::complex<dou ble
{
public:
operator doublecomplex*( ) { return &m_Data[0]; }
};

I thought this would be sufficient, but (as I'm sure you all know),
the specialized class does not "inherit" anything from the
unspecialized class. So in order to make this work, I would have to
re-implement all my other operators in the specialized class. That
doesn't seem like a good solution to me.
You could inherit...
So I've been coming up with a reasonable way to avoid having to
rewrite a bunch of code and I'm just stumped. I thought maybe
inheritance could solve my problem, but I don't see how. I thought
about trying to combine templates and in heritance, but I'm not sure
how that would work, either.

If anyone has any suggestions, I'd love to hear them.
Yes, inherit. Lessee...

template<class Tstruct MyT_Base {
// all the stuff
};

template<class Tstruct MyT : MyT_Base<T{
operator T*() { return 0; } // special stuff
};

template<struct MyT<double: MyT_Base<double {
operator float*() { return 42.; } // special stuff
};

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Sep 8 '06 #2
Victor Bazarov wrote:
template<class Tstruct MyT_Base {
// all the stuff
};
template<class Tstruct MyT : MyT_Base<T{
operator T*() { return 0; } // special stuff
};
template<struct MyT<double: MyT_Base<double {
operator float*() { return 42.; } // special stuff
};
Victor, this is a great idea. And it works really well. I just have
one lingering question.

The base class has a constructor that takes 2 ints. As far as I can
tell, I have to implement similiar constructors in the derived classes
that do nothing but call the base class's constructor. This isn't a
really big deal (and it's a VAST improvement over the way it was
before), but I'm wondering if there's any way around it. I tried a
using clause. That didn't get me very far. :-)

Thanks,
Bill

Sep 8 '06 #3

wo******@gmail. com wrote:
Victor Bazarov wrote:
template<class Tstruct MyT_Base {
// all the stuff
};
template<class Tstruct MyT : MyT_Base<T{
operator T*() { return 0; } // special stuff
};
template<struct MyT<double: MyT_Base<double {
operator float*() { return 42.; } // special stuff
};

Victor, this is a great idea. And it works really well. I just have
one lingering question.

The base class has a constructor that takes 2 ints. As far as I can
tell, I have to implement similiar constructors in the derived classes
that do nothing but call the base class's constructor. This isn't a
really big deal (and it's a VAST improvement over the way it was
before), but I'm wondering if there's any way around it. I tried a
using clause. That didn't get me very far. :-)
You can inherit in the other direction. If you need access to members
of your object in the cast operator you can use the curriously
reoccuring template pattern...look it up in google. Also look up
policy based programming - you would basically be creating a "casting
policy" and inheriting from it to gain that behavior.

Sep 8 '06 #4
wo******@gmail. com wrote:
Victor Bazarov wrote:
>template<cla ss Tstruct MyT_Base {
// all the stuff
};
template<cla ss Tstruct MyT : MyT_Base<T{
operator T*() { return 0; } // special stuff
};
template<struc t MyT<double: MyT_Base<double {
operator float*() { return 42.; } // special stuff
};

Victor, this is a great idea. And it works really well. I just have
one lingering question.

The base class has a constructor that takes 2 ints. As far as I can
tell, I have to implement similiar constructors in the derived classes
that do nothing but call the base class's constructor. This isn't a
really big deal (and it's a VAST improvement over the way it was
before), but I'm wondering if there's any way around it. I tried a
using clause. That didn't get me very far. :-)
Yes, you'll have to reimplement. Constructors are not inherited.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Sep 8 '06 #5
wo******@gmail. com wrote:
The base class has a constructor that takes 2 ints. As far as I can
tell, I have to implement similiar constructors in the derived classes
that do nothing but call the base class's constructor. This isn't a
really big deal (and it's a VAST improvement over the way it was
before), but I'm wondering if there's any way around it. I tried a
using clause. That didn't get me very far. :-)
If you have to write the base class constructor(s), then you have to
write the derived class constructor(s).

It might be possible to avoid having to write the base class
constructor(s), in which case you won't have to write the derived class
constructor(s).

Can you show us the code? If so, it would be interesting to see the
member variables, the constructor(s), the class copy assignment
operator, and the destructor.
Sep 8 '06 #6
Howard Gardner wrote:
Can you show us the code? If so, it would be interesting to see the
member variables, the constructor(s), the class copy assignment
operator, and the destructor.
Sure. Just... be kind. I'm a mathematician, not a programmer. :-p
In all honesty, it's already great the way it is. This is supposed to
be a really light-weight class that just makes it easier to do stuff
with LAPACK.

Here's what I have thus far:

template<typena me T>
class MatrixBase
{
public:
T& operator()(int iRow, int iColumn) { return m_Data[m_iRows *
iColumn + iRow]; }

int GetRows() const { return m_iRows; }

int GetColumns() const { return m_iColumns; }

protected:
MatrixBase(int iRows, int iColumns): m_iRows(iRows),
m_iColumns(iCol umns), m_Data(iRows * iColumns) {}

std::vector<Tm_ Data;

private:
int m_iRows;
int m_iColumns;
};

template<typena me T>
class Matrix: public MatrixBase<T>
{
public:
Matrix(int iRows, int iColumns): MatrixBase<T>(i Rows, iColumns) {}

operator T*() { return &m_Data[0]; }
};

template<>
class Matrix<std::com plex<double: public
MatrixBase<std: :complex<double
{
public:
Matrix(int iRows, int iColumns): MatrixBase<std: :complex<double >
>(iRows, iColumns) {}
operator doublecomplex*( ) { return
reinterpret_cas t<doublecomplex *>(&m_Data[0]); }
};

Sep 8 '06 #7
There are some potentially troublesome things in your implementation.
Whether they are real problems depends (as always) on what you mean to
do with it. If it is something that you intend to use once and then
forget about, they probably aren't. If it is something at the heart of
an important program (ie, your thesis work), then they may well be.

Before I get into that, though: have you looked at blitz++? It has an
excellent reputation.

http://www.oonumerics.org/blitz/

Here's another implementation.

The main difference between this one and yours is that this introduces
the "non type template parameters" ROWS and COLUMNS. Using this
implementation, C++ will treat a 2x2 matrix and a 3x3 matrix as
different types. Using your implementation, it treated them as the same
type. The benefit is a cleaner implementation of the functionality that
is here, and I think it will also improve the implementation of the
functionality (the matrix operators) that are not here. The cost is that
this version might use more memory than your version.

A second important difference is that I've bounds-checked the operator()
for accessing the elements. If you specify an index out of bounds then
it will throw an exception. There is a performance penalty for this.

I've commented the reinterpret_cas t out because I don't have the library
that you're using. That function is indispensable to your purpose, but
it contains two assumptions: first that doublecomplex is bit compatible
with std::complex<do uble>, and second that std::vector is storing its
data as an array. Your program works, so the assumptions are true for
your current combination of compiler/libraries. It might not be true
with a different combination.

#include<vector >
#include<comple x>
#include<algori thm>
#include<stdexc ept>

#include<ostrea m// for testing

// ----------------------------------
template<typena me T, unsigned ROWS, unsigned COLUMNS>
class MatrixBase
{
public:
MatrixBase();
T& operator()(int iRow, int iColumn);
static const unsigned rows = ROWS;
static const unsigned columns = COLUMNS;
protected:
std::vector<Tm_ Data;
~MatrixBase();
};

// ---
template<typena me T, unsigned ROWS, unsigned COLUMNS>
MatrixBase<T, ROWS, COLUMNS>::Matri xBase()
:
m_Data(rows * columns)
{
}

// ---
template<typena me T, unsigned ROWS, unsigned COLUMNS>
T& MatrixBase<T, ROWS, COLUMNS>::opera tor()(int iRow, int iColumn)
{
if(iRow >= rows) throw std::range_erro r("row");
if(iColumn >= columns) throw std::range_erro r("columns");
return m_Data[rows * iColumn + iRow];
}

// ---
template<typena me T, unsigned ROWS, unsigned COLUMNS>
MatrixBase<T, ROWS, COLUMNS>::~Matr ixBase()
{
}

// ---
template<typena me S, typename T, unsigned ROWS, unsigned COLUMNS>
S& operator<<(S & stream, MatrixBase<T, ROWS, COLUMNS& matrix)
{
for(unsigned row = 0; row < matrix.rows; ++row)
{
for(unsigned column = 0; column < matrix.columns; ++column)
{
stream << matrix( row, column ) << ' ';
}
stream << '\n';
};

return stream;
}

// ----------------------------------
template<typena me T, unsigned ROWS, unsigned COLUMNS>
class Matrix: public MatrixBase<T, ROWS, COLUMNS>
{
public:
operator T*() { return &MatrixBase< T, ROWS, COLUMNS>::m_Dat a[0]; }
};

// ----------------------------------
template< unsigned ROWS, unsigned COLUMNS >
class Matrix<std::com plex<double>, ROWS, COLUMNS>: public
MatrixBase<std: :complex<double >, ROWS, COLUMNS>
{
public:
// operator doublecomplex*( );
};

// ---
//Matrix<std::com plex<double::op erator doublecomplex*( )
//{
// return reinterpret_cas t<doublecomplex *>(&m_Data[0]);
//}
// Test code below this line //
template< typename T, unsigned ROWS, unsigned COLUMNS >
Matrix< T, ROWS, COLUMNS func( Matrix< T, ROWS, COLUMNS & a )
{
return a;
}

int main(void)
{
using std::cout;
using std::endl;

Matrix< int, 2, 2 a;
a( 0, 1 ) = 1;
a( 1, 0 ) = 2;
a( 1, 1 ) = 3;
cout << a << endl;

Matrix< int, 2, 2 b(a);
Matrix< int, 2, 2 c;
c = a;

Matrix< std::complex< double >, 20, 20 d;
d( 5, 5 ) = std::complex< double >( 1, 1 );
cout << d << endl;

Matrix< std::complex< double >, 20, 20 e(d);
cout << e << endl;
}
Sep 8 '06 #8

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
2544
by: SainTiss | last post by:
Hi, If you've got a template class with lots of methods, and then you've got a type which works with the template, except for one method... What you need to do there is specialize the template for your type. However, this requires you to copy the whole template, and change the method, which leads to code duplication... If there's only 1 template parameter, one can specialize the method
8
7689
by: Agent Mulder | last post by:
Hi group, I have a problem with partial template specialization. In the code below I have a template struct Music with one method, play(), and three kinds of music, Jazz, Funk and Bach. When I specialize Music<Bach>, I expect that the original play() method is available in the specialization, but it is not. How can I fix this? -X
2
5780
by: Jeff | last post by:
/* -------------------------------------------------------------------------- Hello, I was experimenting with class templates and specializing member functions and came across a simple problem which I can't figure out. I have a simple class C with 2 template member objects, and a function print() that prints the value of these objects. I defined a specialization of print() for C<string, char> which is called correctly. Then I wanted...
9
2785
by: Marek Vondrak | last post by:
Hello. I have written the following program and am curious why it prints "1" "2". What are the exact effects of explicitly providing function template parameters at the call? Is the second assign() function really a specialization of the first assign() or is it an assign() overload? Thank you. -- Marek
2
2498
by: Thomas Kowalski | last post by:
Hi, I would like to write a template class Polygon<VertexTypthere vertex typ can be eigther a pointer or a value typ. It has an attribute: std::vector<VertexTypvertices; And a methode: VertexValueTyp& vertex(int i); that dereferences vertices internally in case VertexTyp is a pointer
9
3472
by: stephen.diverdi | last post by:
Can anyone lend a hand on getting this particular template specialization working? I've been trying to compile with g++ 4.1 and VS 2005. //------------------------------------------------------------------ // my regular glass class A { }; // my templated class
2
7699
by: Barry | last post by:
The following code compiles with VC8 but fails to compiles with Comeau online, I locate the standard here: An explicit specialization of any of the following:
8
2969
by: flopbucket | last post by:
Hi, I want to provide a specialization of a class for any type T that is a std::map. template<typename T> class Foo { // ... };
1
2228
by: Ioannis Gyftos | last post by:
Hello, First the code :) /////////////////////////////////////////////////////////////////////////////////// // in another header file namespace LJC{
6
2619
by: abir | last post by:
i have a template as shown template<typename Sclass Indexer{}; i want to have a specialization for std::vector both const & non const version. template<typename T,typename Aclass Indexer<std::vector<T,A {} matches only with nonconst version. anyway to match it for both? and get if it is const or nonconst? Actually i want 2 specialization, one for std::vector<T,Aconst & non const other for std::deque<T,Aconst & non const.
0
9483
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10346
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
10157
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
0
9956
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
8982
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7504
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5386
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5514
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
3
2887
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.