The function, std::vector.push_back takes a paramater of the type: [const t &]. with t being the type that the vector was constructed with. What I need to do is convert [*this] to a [const t &]. Is there any way that I can do this?

This is the code that I have:

template< typename t >
class CSpawnableEntity{
	STR m_sSpawnName;
public:
	CSpawnableEntity( char *pSpawnName )
		:m_sSpawnName( pSpawnName )
	{
		gpGlobals.entities.push_back( *this  );
	}

	t New( ){ return new t; }
};

gpGlobals.entities is of the type std::vector< CSpawnablyEntity< CBase * > >. I need to know how I can get that code to compile by doing the things that I said above.

Recommended Answers

All 10 Replies

I mis-interpretted your question intially

Are you trying to make a self regitering class
in which case you probably want a vector of pointers
not objects

The problem is where do you define gpGlobals

when you create an instance of a class the template has to be defined
now as your code is in the constructor

vector<Spawnable<int>> gpPointers cannot be defined until apawnable is defined

and therefore cannot be used by Spawnable in its constructor

you can either have a vector<void *> and cast as needed dangerous

or have a different design so that you can

have a register method instead of just a constructor

/*templ_s.h all functions must be defined in .h
for templates
*/

#pragma once
#include <vector>

template< typename t>
//std::vector<templ_s<t> *> gpPointers;

class templ_s
{
public:
 typedef templ_s<t> tt;
 templ_s()
 {
 }
  
 void register_method(std::vector<tt *>  &my_pointers)
 {
   my_pointers.push_back(this);
 }

protected:
	t var;
};
#include "templ_s.h"
int main()
{
 templ_s<int> test;
 std::vector<templ_s<int> *> v_test;
 test.register_method(v_test);
 return 0;
}

otherwise a different method of handling is required and you need to
be very precise in your design requirements

<nevermind>

I had a bit of a further play around with some of the code
and I suspect that there is part of your design that isn't quite right and that your class will struggle to do what you want.

This is an area where I haven't done much coding mainly because I always want to mix virtuals, statics and templates and the compiler
normally gives up and cry.

The problem with your design is that you have a class and
you want it to automatically add itself to a vector.

Now the problem is what do you want to happen when a variable dies. Either you need a class to manage the variables and tidy up rather than having a global.

Or the alternative I tried to write was to have a class
that managed itself but it still required a global to handle the static
member variable and therefore not fully templated as this would
have to be defined outside the class;

Therefore to template the my_tracker variable needs to be moved but I could not get it to work as a static variable of a template function probably can extern with a namespace but it all gets pretty ugly

tracker.h

#pragma once
#include <vector>

class tracker
{
public:
  tracker();
  ~tracker();
  int add(void * pv);
  std::vector<void *> * get_pointers();
  void del(int id);
private:
  std::vector<int> my_ids;
  std::vector<void *> * my_pointers;
  int next_id;
};

extern tracker*  my_tracker; //this is in the wrong place

tracker.cpp
keeps track of which objects are alive

#include "tracker.h"

tracker::tracker()
:next_id(0)
{
	my_pointers = new std::vector<void *>; 
}

int tracker::add(void * pv)
{
	int ret(-1);
	if(pv != 0)
	{
		ret = next_id;
		my_ids.push_back(next_id);
		my_pointers->push_back(pv);
		++next_id;
	}
	return ret;
}

void tracker::del(int id)
{
std::vector<void *>::iterator it_p(my_pointers->begin());
std::vector<int>::iterator it_stop(my_ids.end());
for(std::vector<int>::iterator it(my_ids.begin()); it!= it_stop; ++it)
 {
    if(id == *it)
   {
      my_ids.erase(it);
      my_pointers->erase(it_p);
    }
    ++it_p;
  }
}


std::vector<void *> * tracker::get_pointers()
{
  return my_pointers;
}

tracker::~tracker()
{
  delete my_pointers;
}
//again this shouldn't be here
tracker *  my_tracker = new tracker();

templ_s.h
this will now self register and can call get_pointers as
a gloabal method but because my_tracker is in the wrong place
it will break if you have two different templates at once :(

pragma once
#include <string>
#include "tracker.h"

template <typename t>

class templ_s
{
public:
typedef templ_s<t> tt;

templ_s()	
{
  if(my_tracker == 0)
  {
   my_tracker = new tracker();	
  }
  my_id = my_tracker->add(this);
}

void set_name(char * pc)
{
  my_name = pc;
}

std::string get_name()
{
  return my_name;
}

~templ_s()
{
  my_tracker->del(my_id);
}

//a static method to get the pointers as if it was a global
static std::vector<tt *> get_pointers()
{
std::vector<tt *> ret;
if(my_tracker != 0)
{
  std::vector<void *> * pvv = my_tracker->get_pointers();
			
  if(pvv != 0)
 {
    int sz = pvv->size();	
    for(int i = 0; i < sz; ++i)
    {
       tt * ptt =  static_cast<tt *>(pvv->operator[](i));
       ret.push_back(ptt);
     }
   }
 }
  return ret;
}
	
private:
//wanted static tracker *my_tracker
 std::string my_name;//so that
 int my_id;
};

finally an example of it working:

#include <iostream>
#include "templ_s.h"
#include "tracker.h"

int main()
{
 char fill_value = '$';

 templ_s<int> test1, test2;
 test2.set_name("I am two");
 test1.set_name("I am one");
 {//scoping test 3
   templ_s<int> test3;
   test3.set_name("I am three");
std::vector<templ_s<int> *>  v_test = templ_s<int>::get_pointers();
   int sz = v_test.size();
   std::cout << "size == " << sz << std::endl;
 for(int k = 0; k < sz; ++k)
 {
   std::cout << v_test.operator[](k)->get_name() << std::endl;;
}

}
std::vector<templ_s<int> *>  v_test = templ_s<int>::get_pointers();
int sz = v_test.size();
std::cout << "size == " << sz << std::endl;
for(int m = 0; m < sz; ++m)
{
std::cout << v_test.operator[](m)->get_name() << std::endl;;
}
return 0;
}

I still don't understand why this doesn't work. I changed it so that it should work, but it's complaining about converting it from a "const CSpawnableEntity<t>"(which is what i have) to a "const CSpawnableEntity<t> &" (which is what it takes). How do I make it an address? Using the * operator on the left hand side doesn't work, using the & operator doesn't work. What will?

BTW I fixed the problem with defining gpGlobals after the class.

With template classes you have to be very wary about compiler error messages. The code only gets built when it is needed and so the compiler does not compile using the template version but only when it knows what the type is.

You are trying to the following and the compiler is ignoring
it until you try to use it.

class c
{
public:
 c()
 {
     /*
         You cannot call the constructor  c()
         here as it will be an infinite loop
         if you have a std::vector<c> vc;
         when you try to add a c it is effectively
     */
      c x; //cannot call me infinite loop
      vc.push_back(x);
     /*
       therefore vc cannot work until c() is defined 
      a c* however can exist before c();
       but you only should add from new
     */
 }
int my_data;
};

you get the address from the instance not the compiler
int x(7);
int * px = &x;
There is no problem in declaring a vector to take a copy of an object
but your logic is inside out with the vector here which was the point I was trying to make earlier. You normally would not even want to have the functionality I showed in my second post.

The constructor doesn't know its own address if you want an address you need to call the new() via a factory method.

class c
{
private:
 c()
{
pointer = 0;
}

public:
static c *  get_me()
{
   if(pointer != 0)
   {
       //already defined avoid memory leaks
       //? delete pointer; 
    }
      pointer = new c();
     return pointer;

}


private:
 c * pointer;
};

I still don't understand why this doesn't work. I changed it so that it should work, but it's complaining about converting it from a "const CSpawnableEntity<t>"(which is what i have) to a "const CSpawnableEntity<t> &" (which is what it takes). How do I make it an address? Using the * operator on the left hand side doesn't work, using the & operator doesn't work. What will?

I changed my code to this:

template< typename t >
class CSpawnableEntity{
	STR m_sSpawnName;
	CSpawnableEntity< t > * m_pPointer;
public:
	CSpawnableEntity( char *pSpawnName )
		:m_sSpawnName( pSpawnName )
	{
		m_pPointer = 0;
		gpGlobals.entities.push_back( Me( ) );
	}

	t New( ){ return new t; }
	CSpawnableEntity< t > Me( )
	{
		if( m_pPointer != 0 )
		{
			delete m_pPointer;
		}
		m_pPointer = new CSpawnableEntity< t >( m_sSpawnName );
		return m_nPointer;
	}
};

And I get this error:
error C2664: 'std::vector<_Ty>::push_back' : cannot convert parameter 1 from 'CSpawnableEntity<t>' to 'CSpawnableEntity<t> &'

bump

commented: Never BUMP on this forum. It's frowned upon here. When someone gets to your post, they will answer it. -2

You are still trying to do the same the thing:

Why do you think you need gpglobals in the constructor?

You can get close to this with my second example but it really is unusual behaviour

In my last example I had a static method that probably a static pointer rather than a member variable

rather than calling classname x; directly

you call

classname * x = classname::factory();
x->method();

because factory is static you do not need an instance of the class
so the constructor is never called directly but this causes serious
memory handling problems and gpglobals must be explicitly set for all the types you might need. you can never know what is in gpglobals is valid!


The level of sophistication required to do what you are asking for is mainly so high as a result of you not really want this functionality, especially not templated
what you would have is a function that creates a spawnable and
adds it to a vector ie:

t get(int ind);
void add_t(t & to_add);
std::vector<t> my_vector;

If you read up on singleton methods this will be closer to what you wanted

IF you try the trackable example I gave this is as close to what you stated you wanted that you can get but your design is stil not correct at the paper level which is why you are getting confused.l

Template classes are only there to save you typing code
and in most cases it is not worth using them unless you
fully understand precisely what you need them before


The cleanest solution that is possible with my compiler
still requires that you explicitly declare what classes
you are going to be using via global pointers

tracker class keeps an id paired with a pointer
to allow random deletion and creation of pointers.


to get round the problem of the global in the tracker example I gave
we need a wrapper around tracker to allow one tracker for each type

this class is being called wrapped_tracker() and has methods that
should only be called when you know they are safe.

first declaring the types we will be using thus

wrapped.cpp

#include "wrapped_tracker.h"

wrapped_tracker<int> * wrapped_tracker<int>::my_tracker = 0;
wrapped_tracker<double> * wrapped_tracker<double>::my_tracker = 0;

//would add other types here

so the wrapper class is a singleton method
for each type t used:

wrapped.h

#pragma once
#include "tracker.h"


template <typename t>
class wrapped_tracker
{
private:
  wrapped_tracker()
    : pointer(0), b_tracker(false)
  {
    if(my_tracker != 0)
    {
      //have a tracker make my pointers right
      b_tracker = my_tracker->has_tracker();
      if(b_tracker == true)
      {
        pointer = my_tracker->get_pointer();
      }
    }
  }
  
public:

//factory method
  static wrapped_tracker * create()
  {
    if(my_tracker == 0)
    {
      my_tracker = new wrapped_tracker<t>();
    }
    return my_tracker;
  }

//important as cannot set local pointer in static method
  void init_tracker()
  {
    if(my_tracker == 0)
    {
      my_tracker = new wrapped_tracker<t>();
    }

    if(my_tracker != 0)
    {
      if(my_tracker->has_tracker() == false)
      {
        my_tracker->set_tracker(new tracker());
        pointer = my_tracker->get_pointer();
        b_tracker = true;
      }
    }

  }

  tracker * get_pointer()
  {
    if(b_tracker == false)
    {
//no pointer set here check global
      if(my_tracker != 0)
      {
        if(my_tracker->has_tracker() == true)
        {
          set_tracker(my_tracker->get_pointer());
        }
      }
    }
    return pointer;
  }

//should be private
  void set_tracker(tracker *pt)
  {
    pointer = pt;
    b_tracker = true;
  }

//false does not mean get_traker() == 0
  bool has_tracker()
  {
    return b_tracker;
  }

//declared in wrapped.cpp only use get_pointer();
static wrapped_tracker<t> * my_tracker;

private:
  tracker * pointer;
  bool b_tracker;
  
};

THis code looks messy but there are synchornisation
issues that have to be overcome the designed usage becomes clear
in its implementation

templ_s.h

#pragma once
#include <string>
#include "wrapped_tracker.h"


template <typename t>
class templ_s
{
public:
  typedef templ_s<t> tt;

  templ_s()
  : my_test_tracker(0)
  {
    //using factory method so only create the 1st time  
    my_test_tracker = wrapped_tracker<t>::create();
//need to call to init
    my_test_tracker->init_tracker();
//-> have to be valid unless new() fails
    my_id = my_test_tracker->get_pointer()->add(this);
  }

  void set_name(char * pc)
  {
    my_name = pc;
  }

  std::string get_name()
  {
    return my_name;
  }


//has to be static to call even if no instances around
  static std::vector<tt *> get_pointers()
  {
    std::vector<tt *> ret;
   //cannot use member variables but create works
    wrapped_tracker<t> * temp = wrapped_tracker<t>::create();
    tracker * pt = temp->get_pointer();
    if(pt != 0)
    {
      std::vector<void *> * vv = pt->get_pointers();
      std::vector<void *>::iterator it_stop(vv->end());
      for(std::vector<void *>::iterator it(vv->begin()); it != it_stop; ++it)
      {
//it is lines like this one why tracker is so complicated
        tt * converted = static_cast<tt*> (*it);
        ret.push_back(converted);
      }
    }
    return ret;
  }

  ~templ_s()
  {
    if(my_test_tracker != 0)
    {
//must have pointer
      my_test_tracker->get_pointer()->del(my_id);
    }
  }

  
public:
//local copy but do not own so no call to delete
  wrapped_tracker<t> * my_test_tracker;
//test data
  std::string my_name;
//needed by tracker
  int my_id;
};

now every instance of templ_s can be tracked by its type and
it does not matter which order they are destroyed

#include <iostream>
#include <vector>
#include "templ_s.h"

#include "wrapped_tracker.h"

//const int max_row(5), max_col(5);
//void dummy_get_shape(char shape[max_row][max_col]);
//void display_shape(char shape[max_row][max_col]);

int main()
{
  
  char fill_value = '$';

  wrapped_tracker<int> * wt1 = wrapped_tracker<int>::create();
  wrapped_tracker<double> * wt2 = wrapped_tracker<double>::create();

  templ_s<int> test1, test2;
  test2.set_name("I am two");
  test1.set_name("I am one");
  

  templ_s<double> dt1, dt2;
  dt1.set_name("I am 1.2");
  dt2.set_name("I am 2.1");

  {
    templ_s<int> test3;
    test3.set_name("I am three");
    std::vector<templ_s<int> *>  v_test = templ_s<int>::get_pointers();
    int sz = v_test.size();
    std::cout << "size == " << sz << std::endl;
    for(int k = 0; k < sz; ++k)
    {
      std::cout << v_test.operator[](k)->get_name() << std::endl;;
    }

  }
  std::vector<templ_s<int> *>  v_test = templ_s<int>::get_pointers();
  int sz = v_test.size();
  std::cout << "size == " << sz << std::endl;
  for(int m = 0; m < sz; ++m)
  {
    std::cout << v_test.operator[](m)->get_name() << std::endl;;
  }

  std::cout << "trying for type 2" << std::endl;
  std::vector<templ_s<double> *>  v_dt = templ_s<double>::get_pointers();
  int dsz = v_dt.size();
  std::cout << "size == " << dsz << std::endl;
  for(int d = 0; d < dsz; ++d)
  {
    std::cout << v_dt.operator[](d)->get_name() << std::endl;;
  }

return 0;
}

As you can see this code gets big because you need to avoid
memory leaks and dereferencing null pointers

it successfully keeps track of all live types.


You needed to understand
templates
singleton
static functions
static variables
to follow this code

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.