Sign in to follow this  
Sync Views

template function problem

Recommended Posts

Sync Views    139
object.h
template<typename obj_type>int instance_count();

object.cpp
template<typename obj_type> 
int instance_count()
{
	int count = 0;
	for(std::vector<object *>::iterator it = cgm::object_list.begin(); it != cgm::object_list.end(); it++)
	{	
		if (!(*it)) continue;
		if (!(*it)->active) continue;
		if (!dynamic_cast<obj_type*>(*it)) continue;
		count++;
	}
	return count;
}

somewhere else...
int count = instance_count<obj_target>();

Why do I get linker errors saying it can't link my function? It's clearly there and there are no spelling mistakes or anything...

Share this post


Link to post
Share on other sites
SiCrane    11839
Unless your compiler supports the export keyword, and I'm willing to bet that yours doesn't, then you can't put the definition of a template in a separate source file without explicit instantiation for specific types. Without explicit instantiation, the complete definition of the template needs to be available at point of instantiation, which means, in effect, that the definition needs to go into the header. (Or an inline file of some sort, etc.)

Share this post


Link to post
Share on other sites
Sync Views    139
The problem is then I'd need to make all my object stuff global which I really don't want to do...especially for a static lib...

Is there anyway I can have the template function in the header and have it pass the object type to a another function someother way?

Share this post


Link to post
Share on other sites
Antheus    2409
template<typename obj_type> 
int instance_count()
{
int count = 0;
for(std::vector<object *>::iterator it = cgm::object_list.begin(); it != cgm::object_list.end(); it++)
{
if (!(*it)) continue;
if (!(*it)->active) continue;
if (!dynamic_cast<obj_type*>(*it)) continue;
count++;
}
return count;
}



Put the above in the header. It doesn't change anything whatsoever from what you're doing now.

The function above doesn't exist in lib - it's part of included header file. It'll be instantiated where you call it.

Templates are just a different form of macros.

Even more, you don't need to include vector or object_list or anything similar in the header file. You just need to make sure they are included at point of use.

Templated functions are just templates, pieces of text.

When you call such a function, that particular specialization will be instantiated in the compilation unit where you're using it. This is why one common counter-argument against templates is code bloat. Templates can get instantiated many times across different compilation units.

Share this post


Link to post
Share on other sites
Sync Views    139
if it's in the header though it will fail against every cpp except object.cpp because cgm::object_list doesn't exist anywhere else... I don't really want to make stuff like that global...

Share this post


Link to post
Share on other sites
rip-off    10979
Quote:
Original post by Sync Views
The problem is then I'd need to make all my object stuff global which I really don't want to do...especially for a static lib...

Is there anyway I can have the template function in the header and have it pass the object type to a another function someother way?


Something like this:

// header file
int instance_count(const std::type_info &);

template<typename obj_type>
int instance_count()
{
return instance_count(typeid(obj_type));
}

// source file

int instance_count(const std::type_info &info)
{
int count = 0;
for(std::vector<object *>::iterator it = cgm::object_list.begin(); it != cgm::object_list.end(); it++)
{
object *ptr = *it;
if(ptr && ptr->active && info == typeid(*ptr))
{
count++;
}
}
return count;
}





While this appears to work, it is considered a poor idea to look up the dynamic type of an object instance. The whole point of inheritance is that it shouldn't matter.

One could also note that we are basically re-writing std::count_if, so we could use that instead:

// header file
int instance_count(const std::type_info &);

template<typename obj_type>
int instance_count()
{
return instance_count(typeid(obj_type));
}

// source file
#include <algorithm>

struct Predicate
{
const std::type_info *info;
Predicate(const std::type_info &info):info(&info){}

bool operator()(const object *ptr) const
{
return (ptr && ptr->active && (*info) == typeid(*ptr));
}
};

int instance_count(const std::type_info &info)
{
return std::count_if(cgm::object_list.begin(),cgm::object_list.end(),Predicate(info));
}


Share this post


Link to post
Share on other sites
Sync Views    139
Is there a better way other than template functions or type id's to pass a type and check if that type is the same as the type a pointer is pointing to? Taking into acount inheritance etc?

eg

bool objecttype(void* obj, class obj_type);
{
if(obj == obj_type) return true);
else return false;
}





I only really need to check if the item I'm pointing to is either the same as the objtype or a child of it...

Share this post


Link to post
Share on other sites
SiCrane    11839
Yes, and it's really quite simple: don't lose that type information in the first place. Universal base classes are a bad thing.

Share this post


Link to post
Share on other sites
Antheus    2409
Quote:
Original post by Sync Views
Is there a better way other than template functions or type id's to pass a type and check if that type is the same as the type a pointer is pointing to? Taking into acount inheritance etc?

eg
*** Source Snippet Removed ***

I only really need to check if the item I'm pointing to is either the same as the objtype or a child of it...


Templates are compile-time polymorphism. You need to provide the type you're using.

If you want inheritance and all that, you need run-time polymorphism, where you do not know the type ahead.

Decide which you want. Mixing both is generally not needed, especially not in the way you want.

void * is always bad, except under certain circumstances for internal usage. But in general, you should never (in C++) need this in user API.

Quote:
I only really need to check if the item I'm pointing to is either the same as the objtype or a child of it...


Why not imply that?
void foo(BaseType * bar)
{
// whatever, but bar is of type BaseType
};


Here you'd get compile-time check if you pass incorrect instance.

Obviously, bars will come from, for example, std::vector<BaseType *>. C++ provides type safety. Use it.

Share this post


Link to post
Share on other sites
rip-off    10979
I don't get it. A type_info object maps to a class, one for one. What is wrong with the solution I posted? It would appear to be what you are looking for, if in a somewhat verbose form. C++ is like that.

Share this post


Link to post
Share on other sites
Sync Views    139

class ball
{
...
};
class ball_rugby : public ball
{
...
};
class ball_tenis : public ball
{
...
};
class ball_football : public ball
{
...
};
class ball_basketball : public ball
{
...
};




I need instance_count(typeid(ball)) to include all the other balls parented to it.

Share this post


Link to post
Share on other sites
rip-off    10979
Given that you want to implement the function in a source file (so as not to expose globals) the best you could do might be this then:

// header

template< typename T >
bool instance_of( const object *object )
{
return dynamic_cast<const T *>(object);
}

typedef bool (*InstanceTest)(const object *);
int instance_count( InstanceTest test );

// source file
int instance_count( InstanceTest test )
{
return std::count_if(cgm::object_list.begin(),cgm::object_list.end(),test);
}

// client code:
int ballCount()
{
return instance_count(instance_of<ball>);
}




C++ lacks the reflective features required (that most more modern languages have) to do what you want to do in an easier fashion.

Share this post


Link to post
Share on other sites
Sync Views    139
Well is there a good way to keep track of this infomation? Right now I'm thinking of giveing each object an id for some 2d vector containing list of all the instance of an object useing contructure and decontructures to update it...

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this