|
In addition to the post from Ash, you could get to it like this
class base {};
template< class T >
class CTest : public base
{
}
std::vector<base*> all;
and make base the interface to access what you need in the template classes.
|
|
|
|
|
Hi,
It was my first work around test. Unfortunately, it didn't work because I wasn't able to
down-cast from the parent class to the child class. I guess the problem arise only when a member
function has the template class in argument...
Here what happens.
class base
{
};
template< class T >
class CTest : public base
{
public:
T mem;
void add( CTest< T > i )
{
mem += i.mem;
}
template < class S >
void add( CTest< S > i )
{
}
};
std::vector<base*> all;
CTest< int > a;
CTest< double > b;
all.push( (base*) &a );
all.push( (base*) &b );
all[0]->add( all[1] );
I hope my example is clear enough...
SV.
modified on Wednesday, August 18, 2010 5:03 PM
|
|
|
|
|
I probably shouldn't encourage this - heterogeneous collections usually means there's something a bit screwy in your design. But where OO and generic programming collide you can use runtime type information:
class test
{
public:
virtual test &operator+=( const test &other ) = 0;
};
template <typename T>
class T_test : public test
{
public:
T_test( T initial ) : value_( initial )
{
}
virtual test &operator+=( const test &other )
{
if( const T_test<T> *p = dynamic_cast<const T_test<T> *>( &other ) )
{
value_ += p->value_;
}
return *this;
}
private:
T value_;
};
Then you can use it something like:
int main()
try
{
T_test<int> a( 100 );
T_test<double> b( 100.0 );
T_test<int> c( 200 );
test *ptrs[] = { &a, &b, &c };
std::vector<test *> all( ptrs, ptrs + 3 );
T_test<int> result( 0 );
for( std::vector<test *>::const_iterator iter = all.begin(); iter != all.end(); ++iter )
{
result += **iter;
}
}
catch( const std::exception &e )
{
std::cout << e.what() << std::endl;
}
catch( ... )
{
std::cout << "Something went wrong, no idea what!" << std::endl;
}
If this does the sort of thing you're after please consider how to get rid of the dynamic_cast. Oh, and if anyone asks who told you to do this please don't mention my name, tell them it's some other random hacker.
Cheers,
Ash
Edited for tabs and adding the catch(...)
|
|
|
|
|
Hi Ash,
I did think to use a dynamic cast, but I didn't manage to quickly find how to structure things to make it works. Your example is great, simple and fully compatible with my code. I found another solution by restructuring my code, but this solution is not so great even if I don't have to rely on dynamic casting.
I'll think about it tomorrow and choose what seems the best solution. Quickly, I think an approach base on your example will be more interesting, but a little slower because of the dynamic casting. Shouldn't be a problem since this is not a critical part of my code.
Thanks a lot...
Steve.
|
|
|
|
|
Dynamic_cast isn't that slow - it's of the same sort of order as a virtual function dispatch which is about the same order as calling a function in a shared library. The one big bugger with it is that the compiler has problems inlining the operation as it's got no idea what's being thrown at it.
10:1 your code's got far grosser inefficiencies in it than a dynamic_cast. Whenever I profile my apps I'm always surprised at where my code spends it's time. Anyway, good luck!
Cheers,
Ash
|
|
|
|
|
You are running on a complex thing (for C++) generically named as "mumtimethod".
C++ function dispatching works with v-tables only for the one and only implicit parameter of a virtual member function (this ).
If you need to dispatch based on two parameters (that is: when not only the object type but also a function parameter is important) you need to go across two indirections. And since V-tables holds only one you have to workaround.
There are various articles about "multimethods" (try google with that key) with different approaches, but they always have to fall into two indirections, two switches, one swith and one indirection etc.
The complexity of the coding process will be in any case N2 (you have to supply implementation for all the combination) and the execution complexity (whatever you use V-function of switches) will always be "two indrections".
My invite is to don't drop solution only becasue they use virtual function or because they use dynamic_cast: the fact that such implemetations are always slower is a mith: they are slower than direct calls, but where indirections are needed, they are fast exactly like whatever other indirection mechanism. If you define a vector of function pointer to dispatch the calls ... like many of those article, with more or less smart or complex way do, you are implementing in source a v-table!
2 bugs found.
> recompile ...
65534 bugs found.
|
|
|
|
|
Thanks for the information.
I'm going to read about that...
SV.
|
|
|
|
|
Adding some food for thought
#include <vector>
class base
{
public:
template<class T>
static void sum(T &dest, const T &src)
{
dest.privateadd(src);
}
template<class T, class U>
static void sum(T &dest, const U &src)
{
}
private:
template<class T>
void privateadd(const T &t)
{
virtualadd(&t);
}
virtual void virtualadd(const base *arg) = 0;
};
template< class T >
class CTest : public base
{
public:
T mem;
private:
virtual void virtualadd(const base *arg)
{
const CTest<T> *other = static_cast<const CTest<T>*>( arg );
mem += other->mem;
}
};
int main()
{
std::vector<base*> all;
CTest<int> a;
CTest<double> b;
base::sum(a, b); base::sum(a, a); base::sum(a, 1); all.push_back(&a); all.push_back(&b); base::sum(*all[0], *all[1]);
base::sum(*all[0], *all[0]);
}
|
|
|
|
|
Pretty cool!
I was wondering how I could get rid of the dynamic_cast but completely missed the idea of using free functions (or statics) to do it.
Cheers,
Ash
|
|
|
|
|
Still, there must be a better way to attack the problem in the first place. The base class get bloated rather quickly when extending it.
I was a bit annoyed though that I couldn't get back to the a.add(b) syntax. One could of course add that method to the CTest<T> class and use it when you don't have to rely on pointers to the base class. But it would have been nice to always be able to use it.
|
|
|
|
|
Thanks everyone for your great comments!!!!!
I just wondering about something based on this code. In fact, I thought of something pretty similar but I didn't even try it since I was thinking that shouldn't work.
When the calls are made :
base::sum(*all[0], *all[1]);
base::sum(*all[0], *all[0]);
I was just thinking that *all[0] and *all[1] will be both of type 'base' than the dest and source of the static function sum will always be the same. Always ending in this function
template< class T >
static void sum(T &dest, const T &src)
{
dest.privateadd(src);
}
and never in this one
template<class T, class U>
static void sum(T &dest, const U &src)
{
}
Ultimately we will enter the virtualadd of class CTest with different type to handle. Then, the static_cast should fail...
Since you both think it will work, I'm just wondering where I'm wrong...
SV.
|
|
|
|
|
Bum, good spot! Hang on, why are you the one asking us questions?
Back to the drawing board...
Ash
|
|
|
|
|
Oh, crap. (I was tempted to say that this new problem could easily be solved with a dynamic_cast, but I will refrain from that )
I will have another look.
|
|
|
|
|
something like this would also work, ít depends on what you are up to...
class base {
public:
virtual void accept(class visitor&) = 0;
};
template<typename T>
class impl : public base {
public:
virtual void accept(class visitor& v);
};
class visitor {
public:
void operator()(base* b) {
b->accept(*this);
}
void visit(impl<int>& pimpl) {
std::cout << "i am an int" << std::endl;
}
void visit(impl<double>& pimpl) {
}
};
template<typename T>
void impl<T>::accept(visitor& v) {
v.visit(*this);
}
void foo() {
std::vector<base*> vec;
vec.push_back(new impl<int>);
vec.push_back(new impl<double>);
visitor v;
std::for_each(vec.begin(), vec.end(), v);
}
modified on Monday, August 30, 2010 1:55 AM
|
|
|
|
|
I was wondering where your post went This looks a lot neater.
The only drawback I can see is that you have to foresee all possible values of T. As you say, it depends on what needs to be solved.
|
|
|
|
|
Yeah, sorry, I am writing this down on my PocketPC so it's bit hard to see what I actually wrote. I removed the old post since I couldn't edit it anymore.
To foresee all values of T is the price for having the automatic dispatching wo. rtti. You could go the acyclic visitor way with one dyna-cast in accept...
|
|
|
|
|
hello guys...can we overload the main()?? It is just a question and i've no intentions to do so??
MY ANSWER:
May be we could do it(i dont know how), but since the main() is the driver of our program so it should be punishable if someone does it
|
|
|
|
|
Nope, you can't overload main(), at least not in standard C++. You might be able to in C (or rather assign some other object to main by tricking the linker) though.
Cheers,
Ash
|
|
|
|
|
overloaded Name wrote: hello guys...can we overload the main()??
Meaning what? Are you wanting to change the name of your program's entry point? Or are you referring to the many faces of main :
void main( void )
int main( void )
void main( int argc )
int main( int argc )
void main( int argc, char *argv[] )
int main( int argc, char *argv[] )
void main( int argc, char *argv[], char *env[] )
int main( int argc, char *argv[], char *env[] )
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"Man who follows car will be exhausted." - Confucius
|
|
|
|
|
main always should have a return type of int. Even Microsoft compilers know that if you tell them to disable MS extensions to the language.
|
|
|
|
|
Aescleal wrote: main always should have a return type of int.
It all depends on what you're doing. I seldom use anything other than void , and for good reason.
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"Man who follows car will be exhausted." - Confucius
|
|
|
|
|
Well personally I usually use a C++ compiler to compile C++, but your milage may vary.
Just out of interest what do you think happens if you write:
int main()
{
}
What's returned to the OS? Do you think it's anything different to what happens if you write:
void main()
{
}
and compile with /Ze on Microsoft's compilers?
|
|
|
|
|
Aescleal wrote: What's returned to the OS?
What you are failing to realize is that I don't care what's returned to the OS (i.e., the invoker), if anything. To put it another way, if all I'm doing is some proof-of-concept code, having main() return and accept nothing is perfectly valid.
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"Man who follows car will be exhausted." - Confucius
|
|
|
|
|
Sounds like an interesting concept - not using code that's marginally shorter, standard and portable. At least it'll help confuse future generations of programmers.
|
|
|
|
|
I don't agree.
While I see the point of returning a value of the OS, I personally don't like 'default 0-return value' for code intended to return nothing.
Moreover, the implicit 'return 0; ' statement in the following code is confusing too (at least IMHO).
int main()
{
}
If the Lord God Almighty had consulted me before embarking upon the Creation, I would have recommended something simpler.
-- Alfonso the Wise, 13th Century King of Castile.
This is going on my arrogant assumptions. You may have a superb reason why I'm completely wrong.
-- Iain Clarke
[My articles]
|
|
|
|