Jump to content
  • Advertisement
Sign in to follow this  
LowRad

Hummm... Templated List, STL, Generics, C++ ...

This topic is 4492 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all, I was playing around with templated lists. I was trying to implements a templated list in C++ that looked like the .NET equivalent. There was 2 methods in the .NET List class that gave and still give me some headhaches: - T Find(Predicate<T> math); - void ForEach(Action<T> match); I finally fix the Find method and it's working like a charm. Now, i'm trying to get the ForEach method to work. Below is my current implementation:
//
// Implements a basic crappy type named A
// -------------------------------------------------
class A
{
protected:	
	int i;

public:
	A();
	A(int value);
	virtual ~A();

public:
	void setInt(const int& value) { i = value; }
	int& getInt() const { return (int &)i; };
};


//
// Defines an Action to be performs on list
// -------------------------------------------------
template <typename T> class Action
{
public:
  virtual void operator() (T& obj) = 0;
};


//
// Implements a basic Adder action for list of A
// -------------------------------------------------
class Adder : public Action<A>
{
public:
  void operator() (A& obj)
  {
    obj.setInt( obj.getInt() + 1 );
  }
};

//
// Defines an generic List of type T
// -------------------------------------------------
template <typename T> class List
{
private:
  std::list<T> innerList;

public:
  /* There's alot of others methods not importants here */

  void ForEach(Action<T>& action)
  {
    std::list<T>::iterator iter;

    for(iter = innerList.begin(); iter != innerList.end(); iter++)
    {
      T item = (T)*iter;
      action(item);
    }
  }
};


//
// The Test Program 
// -------------------------------------------------
int main()
{
  List<A> myList;
  myList.Add(A(0));
  myList.Add(A(1));
  myList.Add(A(2));
  myList.Add(A(3));
  myList.Add(A(4));
  myList.Add(A(5));

  myList.ForEach(Adder());
}


This code compiles and works fine, excepts it doesnt works as expected ;). The list doesn't get "updated". Before and After the call to the ForEach method the list is: 0, 1, 2, 3, 4, 5. It's probably a basic error or misunderstanding. I'm sure it has to do with the std::list that i'm using as my innerList. The code obj.setInt( obj.getInt() + 1 ); works and when i'm back to the ForEach method the referenced object's value is valid, but for some reason the referenced object doesn't get updated in the innerList. Can someone helps me understand why? Thanks, Jonathan

Share this post


Link to post
Share on other sites
Advertisement

Quote:
T item = (T)*iter;
action(item);


In your case, your type T is A.

This means your item is a COPY of the contents of your list.

First, I would advise strongly against using ANY C-style casts in your code. You don't know what a C-style cast does. Use dynamic_cast, reinterpret_cast, static_cast or the good-old implicit casting.

Second, I would advise a slightly different signature for your ForEach method.

The result:

template<typename Action>
void ForEach(Action& action) {
{for(internal_iterator it = innerList.begin(); it != innerList.end(); ++it) {
action(*it);
}}
}


Now anything that accepts the contents of your list can be passed to ForEach.

Also, note that the stl already has a foreach function.

Share this post


Link to post
Share on other sites
[edit:
Quote:
Second, I would advise a slightly different signature for your ForEach method.
[...]

Agreed]

It should be:
for( iter = innerList.begin(); iter != innerList.end(); ++iter ) {
T &item = *iter;
action(item);
}

That said, I don't quite see the purpose of giving std::list a .NET-like interface.


jfl.

Share this post


Link to post
Share on other sites
Also assigning a temporary to a non-constant reference is a big no, no. Only constant references with literals or temporaries have defined behaviour.

Share this post


Link to post
Share on other sites
Quote:
Original post by jflanglois
That said, I don't quite see the purpose of giving std::list a .NET-like interface.


jfl.

++

The STL actually defines functions like find and for_each:

#include <iostream>
#include <list>
#include <algorithm>

// Our crappy class
class Foo
{
public:
Foo(void) : m_value(0) {}
Foo(int value) : m_value(value) {}
~Foo(void) {};

int value(void) const {return m_value;}
void setValue(int value) {m_value = value;}

private:
int m_value;
};

// A crappy functor
struct FooAdder : public std::unary_function<Foo &, void>
{
void operator()(Foo &foo)
{
foo.setValue(foo.value() + 1);
}
};

// A (not so?) crappy functor
struct FooPrinter : public std::unary_function<const Foo &, void>
{
FooPrinter(std::ostream &os) : m_os(os) {};

void operator()(const Foo &foo)
{
m_os << foo.value() << " ";
}

private:
std::ostream &m_os;
};

int main(void)
{
std::list<Foo> fooList;
fooList.push_back(Foo(0));
fooList.push_back(Foo(1));
fooList.push_back(Foo(2));
fooList.push_back(Foo(3));
fooList.push_back(Foo(4));
fooList.push_back(Foo(5));

std::cout << "Before executing FooAdder on the list:" << std::endl;
std::for_each(fooList.begin(), fooList.end(), FooPrinter(std::cout));
std::cout << std::endl;

std::for_each(fooList.begin(), fooList.end(), FooAdder());

std::cout << "After executing FooAdder on the list:" << std::endl;
std::for_each(fooList.begin(), fooList.end(), FooPrinter(std::cout));
std::cout << std::endl;

return 0;
}

Share this post


Link to post
Share on other sites
Quote:
Original post by snk_kid
Also assigning a temporary to a non-constant reference is a big no, no. Only constant references with literals or temporaries have defined behaviour.


*it returns a temporary?

Share this post


Link to post
Share on other sites
For most iterators *iter is a reference. However, for some iterators, notably input iterators that aren't also forward iterators and for the demonic bastard stepchild of the container world, std::vector<bool>, *iter will be a temporary.

Share this post


Link to post
Share on other sites
In those cases, it won't be castable to a T const& nor a T&.

That does make the problem interesting. :)

To solve it, your Action should look more like:

struct Action {
template<typename T>
void operator()(RefOf<T>::type t);
};

so it can accept "pseudo-refs" that override operator T, operator= and copy-constructors.

Share this post


Link to post
Share on other sites
Quote:
Original post by NotAYakk
Quote:
Original post by snk_kid
Also assigning a temporary to a non-constant reference is a big no, no. Only constant references with literals or temporaries have defined behaviour.


*it returns a temporary?


I never saw your code, i was referring to LowRad's code i.e myList.ForEach(Adder()); where ForEach takes a non-constant reference and he is passing a temporary which is bad. If he wants to pass a temporary of polymorphic type and behaves polymorphically he has to use a constant reference.

I wouldn't be surprised if he has similar code scattered around else where.

Share this post


Link to post
Share on other sites
What exactly is the difference between using a const and non-const reference for temporaries in a situation like this? The reference is not being held outside the scope of the function call and I don't see anything wrong with modifying the temporary or calling non-const functions on it.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!