-
interesting templates
I have some code here that has a lot of separate classes with the exact same interface:
class ThreadX { void WaitFor() {} };
class ThreadY { void WaitFor() {} };
class ThreadZ { void WaitFor() {} };
For the rest they have nothing in common, no base class.
Then there's some class That holds pointers to these instances:
class ThreadHolder
{
ThreadX* x;
ThreadY* y;
ThreadZ* z;
};
In the destructor every separate object is deleted by the same template of code:
if (x != NULL)
{
x->WaitFor();
delete x;
x = 0;
}
and so hundreds of the exact same lines.(the real code does more)
So I created a template function now that deletes these objects in a generic way.
template<class T>
void TerminateThread(T*& thread)
{
if (thread != NULL)
{
thread->WaitFor();
delete thread;
thread = 0;
}
}
Works nicely, but I want more.
I now have lots of lines like this:
TerminateThread(x);
TerminateThread(y);
TerminateThread(z);
Can I put this in a loop somehow so I can finally reduce this code to a line of 20?
something like:
array a = { x, y, z };
for each item in array
TerminateThread(a[i]);
I can't figure out how to declare the array because the threads are all different classes.
I hope I'm clear :)
-
Inherit all of the classes from the same base class, something like 'ThreadBase'. Then store the threads as an array of base class pointers. So instead of:
Code:
ThreadX x;
ThreadY y;
ThreadZ z;
Do something like this:
Code:
ThreadBase *Threads[3];
ThreadBase[0]=new ThreadX;
That's approximate but I think it's a good idea.
-
yeas, ok that's what I wanted to do, but then I would have to change all the Thread classes.
I was thinking if there's a way with templates for instance so I only have to change this one source file and not the whole project.
-
I think it would be a better idea to change all the Thread classes to have the same base class. It wouldn't take that long, because the changes need be made to only a small portion of the code, and plus, it's good programming practise to share a base class between classes that have some common functionality.
All you need to do is add the base class in, like so:
Code:
class ThreadBase
{
public:
ThreadBase() {}
~ThreadBase() {}
};
And change the Thread class declarations like so:
Code:
class ThreadX : public ThreadBase
{
public:
//etc
Now, you can store a pointer to a ThreadBase object, and when you 'delete' it, the magical V-Tables (or whatever) will ensure that the appropriate destructor for that particular object is run.
I don't think a solution with templates exists here, but someone else may know of a way.
-
You can kind of do it by storing a list of function objects. Something like this
Code:
#include <vector>
#include <iostream>
using namespace std;
class basic_thread_action
{
public:
virtual void del() = 0;
virtual ~basic_thread_action() { }
};
template<typename ThTyp_>
class thread_action : public basic_thread_action
{
public:
typedef ThTyp_ Thread;
thread_action(Thread* t)
: thread(t)
{ }
virtual void del()
{
if (thread != NULL)
{
delete thread;
thread = 0;
}
}
private:
Thread* thread;
};
class thread_holder
{
typedef std::vector<basic_thread_action*> container;
typedef container thread_container;
thread_container threads;
public:
~thread_holder()
{
typedef container::iterator TI;
for (TI i = threads.begin(); i != threads.end(); ++i)
{
(*i)->del();
delete *i;
}
}
template<typename ThTyp_>
void register_thread(ThTyp_* t)
{
threads.push_back(new thread_action<ThTyp_>(t));
}
};
class Thread_a
{
public:
~Thread_a()
{
std::cout << "Thread_a << " << std::endl;
}
};
class Thread_b
{
public:
~Thread_b()
{
std::cout << "Thread_b" << std::endl;
}
};
int main()
{
thread_holder th;
th.register_thread(new Thread_a());
th.register_thread(new Thread_b());
return 0;
}
-
That's a pretty good solution. I would personally just use base classes, but your method would be good in other circumstances, when you have to indiscriminately store objects with no commonality.
-
I think there might be another way as well.
Code:
#include <vector>
#include <iostream>
struct Thread_a
{
~Thread_a()
{
std::cout << "Thread_a" << std::endl;
}
};
struct Thread_b
{
~Thread_b()
{
std::cout << "Thread_b" << std::endl;
}
};
template<typename ThTyp>
struct ThreadStore
{
std::vector<ThTyp*> threads;
~ThreadStore()
{
typedef std::vector<ThTyp*>::iterator TI;
for (TI i = threads.begin(); i != threads.end(); ++i)
delete *i;
}
};
struct ThreadHolder
{
template<typename ThTyp>
static void registerThread(ThTyp* t)
{
static ThreadStore<ThTyp> threads;
threads.threads.push_back(t);
}
};
int main(void)
{
ThreadHolder::registerThread(new Thread_a());
ThreadHolder::registerThread(new Thread_b());
ThreadHolder::registerThread(new Thread_a());
return 0;
}
The code should create for Thread_a and Thread_b two different static instantiation functions with the two different vectors then when the destructors get called it's possible to get what appears to be polymorphic behavior.
-
So if I understand templates correctly, a registerThread() function will be created for each type that is used with it. Is that correct?
-
This is my understanding. You can kind of check what happens by printing out the size if vector threads. On my compiler you get 0, 0, 1.