TObjArray with BOOST_FOREACH

Dear all!

I have to do a lot of looping over TObjArray. I was thinking about using boost’s foreach macro:

boost.org/doc/libs/1_39_0/do … reach.html

Did anybody try to extend boost to allow looping over TObjArray or other root collections?

boost.org/doc/libs/1_39_0/do … ility.html

Thanks in advance,

Sebastian

Hi,

I do not know whether it has been done. However the member function Begin and End and the typedef TObjArray::Iterator_t are the ingredient you will need to implement it.

Cheers,
Philippe.

Hi Philippe!

Unfortunately I don’t find Begin and End member functions. Neither in TObjArray(Iter) nor TIterator (I checked the source). I’m also wondering if an operator++ is missing.

Thanks,

Sebastian

PS: I’m using v5-26-00b, but I also don’t see and Begin/End in root.cern.ch/svn/root/trunk

Hi,

Begin and End are defined in one the base class (TCollection) and the iterator for TObjArray (TObjArrayIter) does inherit from std::iterator should it should work properly (because the internal storage of TObjArray is similar to the storage for a vector<TObject*>)

Cheers,
Philippe.

Hi!

Thanks. TIter is a bit hidden. The implementation so far:

// foreach_TObjArray.cc
#include <iostream>
#include <boost/foreach.hpp>
#include "TObjArray.h"
#include "TVector3.h"

using namespace ROOT;

TIter range_begin( const TObjArray & x ) {

  return TIter(&x).Begin();

}

TIter range_end( const TObjArray & x ) {
  
  return TIter(&x).End();
  
}

bool operator==(const TIter& lhs, const TIter& rhs) {

  return !(lhs != rhs);

}

namespace boost
{
    // specialize range_mutable_iterator and range_const_iterator in namespace boost
    template<>
    struct range_mutable_iterator< TObjArray >
    {
        typedef TIterCategory<TObjArray> type;
    };
  
}

int main() {

  TObjArray objArray;

  objArray.Add(new TVector3(1,2,3));

  boost::BOOST_FOREACH(TObject* s, objArray) {

    std::cout << s << '\n';

  }

}

I couldn’t find a const version of TIter, but I’m not sure if that’s a problem.

I compile it like this:

g++ `root-config --libs --cflags` -I/usr/include/boost foreach_TObjArray.cc -o foreach_TObjArray

But I get the following error during compilation:

foreach_TObjArray.cc: In function ‘int main()’:
foreach_TObjArray.cc:43: error: expected unqualified-id before ‘if’
foreach_TObjArray.cc:43: error: expected ‘;’ before ‘if’
foreach_TObjArray.cc:43: error: ‘else’ without a previous ‘if’
foreach_TObjArray.cc:43: error: ‘_foreach_col43’ was not declared in this scope
foreach_TObjArray.cc:43: error: invalid conversion from ‘const TObject*’ to ‘TObject*’
In file included from foreach_TObjArray.cc:2:
/usr/local/include/boost/foreach.hpp: In function ‘typename boost::foreach_detail_::foreach_reference<T, C>::type boost::foreach_detail_::deref(const boost::foreach_detail_::auto_any_base&, boost::foreach_detail_::type2type<T, C>*) [with T = TObjArray, C = mpl_::bool_<false>]’:
foreach_TObjArray.cc:43:   instantiated from here
/usr/local/include/boost/foreach.hpp:750: error: invalid initialization of non-const reference of type ‘const TObject*&’ from a temporary of type ‘TObject*’

Any idea why

error: invalid initialization of non-const reference of type ‘const TObject*&’ from a temporary of type ‘TObject*’

happens?

Thanks,

Sebastian

Hi,

I don’t know for sure but is likely linked to :[quote]foreach_TObjArray.cc:43: error: invalid conversion from ‘const TObject*’ to ‘TObject*’[/quote]. You would need to look carefully at the boost implementation to figure this out.

Cheers,
Philippe.

Hi!

Just for the record.

error: invalid conversion from ‘const TObject*’ to ‘TObject*’

disappears changing

boost::BOOST_FOREACH(TObject* s, objArray)

to

boost::BOOST_FOREACH(const TObject* s, objArray)

but the other error stays.

Cheers,

Sebastian

First things first :slight_smile:. Where does[quote]foreach_TObjArray.cc:43: error: expected unqualified-id before ‘if’[/quote] come from?

Philippe.

Hi!

You are right. The “boost::” in front of the macro is of course wrong. Here is the version which has only the last error.

#include <iostream>
#include <boost/foreach.hpp>
#include "TObjArray.h"
#include "TVector3.h"

//using namespace ROOT;


TIterCategory<TObjArray> range_begin( TObjArray & x ) {

  return TIterCategory<TObjArray>(&x).Begin();

}

TIterCategory<TObjArray> range_end( TObjArray & x ) {
  
  return TIterCategory<TObjArray>(&x).End();
  
}

/*

TIterCategory<TObjArray> range_begin( TObjArray const  & x ) {

  return TIterCategory<TObjArray>(&x).Begin();

}

TIterCategory<TObjArray> range_end( TObjArray const  & x ) {
  
  return TIterCategory<TObjArray>(&x).End();
  
}

*/

bool operator==(const TIterCategory<TObjArray>& lhs, const TIterCategory<TObjArray>& rhs) {

  return !(lhs != rhs);

}

namespace boost
{
    // specialize range_mutable_iterator and range_const_iterator in namespace boost
    template<>
    struct range_mutable_iterator<TObjArray >
    {
      typedef TIterCategory<TObjArray> type;
    };

    template<>
    struct range_const_iterator<TObjArray >
    {
      typedef TIterCategory<TObjArray> type;
    };

}

int main() {

  TObjArray objArray;

  objArray.Add(new TVector3(1,2,3));

  BOOST_FOREACH(const TObject* s, objArray) {

    std::cout << s << '\n';

  }

}
$ g++ -Wall `root-config --libs --cflags` -I/usr/include/boost foreach_TObjArray.cc -o foreach_TObjArray
In file included from foreach_TObjArray.cc:2:
/usr/local/include/boost/foreach.hpp: In function ‘typename boost::foreach_detail_::foreach_reference<T, C>::type boost::foreach_detail_::deref(const boost::foreach_detail_::auto_any_base&, boost::foreach_detail_::type2type<T, C>*) [with T = TObjArray, C = mpl_::bool_<false>]’:
foreach_TObjArray.cc:66:   instantiated from here
/usr/local/include/boost/foreach.hpp:750: error: invalid initialization of non-const reference of type ‘const TObject*&’ from a temporary of type ‘TObject*’

/usr/local/include/boost/foreach.hpp:750

template<typename T, typename C>
inline BOOST_DEDUCED_TYPENAME foreach_reference<T, C>::type
deref(auto_any_t cur, type2type<T, C> *)
{
    typedef BOOST_DEDUCED_TYPENAME foreach_iterator<T, C>::type iter_t;
    return *auto_any_cast<iter_t, boost::mpl::false_>(cur);
}

(the return is line 750)

The preprocessor (g++ -E …) turns the macro into:

int main() {

  TObjArray objArray;

  objArray.Add(new TVector3(1,2,3));

  if (boost::foreach_detail_::auto_any_t _foreach_col66 = boost::foreach_detail_::contain( (objArray) , (true ? 0 : boost::foreach_detail_::or_( boost::foreach_detail_::and_( boost::foreach_detail_::not_(boost::foreach_detail_::is_array_(objArray)) , (true ? 0 : boost::foreach_detail_::is_rvalue_( (true ? boost::foreach_detail_::make_probe(objArray) : (objArray)), 0))) , boost::foreach_detail_::and_( boost::foreach_detail_::not_(boost_foreach_is_noncopyable( boost::foreach_detail_::to_ptr(objArray) , boost_foreach_argument_dependent_lookup_hack_value)) , boost_foreach_is_lightweight_proxy( boost::foreach_detail_::to_ptr(objArray) , boost_foreach_argument_dependent_lookup_hack_value)))))) {} else if (boost::foreach_detail_::auto_any_t _foreach_cur66 = boost::foreach_detail_::begin( _foreach_col66 , (true ? 0 : boost::foreach_detail_::encode_type(objArray, boost::foreach_detail_::is_const_(objArray))) , (true ? 0 : boost::foreach_detail_::or_( boost::foreach_detail_::and_( boost::foreach_detail_::not_(boost::foreach_detail_::is_array_(objArray)) , (true ? 0 : boost::foreach_detail_::is_rvalue_( (true ? boost::foreach_detail_::make_probe(objArray) : (objArray)), 0))) , boost::foreach_detail_::and_( boost::foreach_detail_::not_(boost_foreach_is_noncopyable( boost::foreach_detail_::to_ptr(objArray) , boost_foreach_argument_dependent_lookup_hack_value)) , boost_foreach_is_lightweight_proxy( boost::foreach_detail_::to_ptr(objArray) , boost_foreach_argument_dependent_lookup_hack_value)))))) {} else if (boost::foreach_detail_::auto_any_t _foreach_end66 = boost::foreach_detail_::end( _foreach_col66 , (true ? 0 : boost::foreach_detail_::encode_type(objArray, boost::foreach_detail_::is_const_(objArray))) , (true ? 0 : boost::foreach_detail_::or_( boost::foreach_detail_::and_( boost::foreach_detail_::not_(boost::foreach_detail_::is_array_(objArray)) , (true ? 0 : boost::foreach_detail_::is_rvalue_( (true ? boost::foreach_detail_::make_probe(objArray) : (objArray)), 0))) , boost::foreach_detail_::and_( boost::foreach_detail_::not_(boost_foreach_is_noncopyable( boost::foreach_detail_::to_ptr(objArray) , boost_foreach_argument_dependent_lookup_hack_value)) , boost_foreach_is_lightweight_proxy( boost::foreach_detail_::to_ptr(objArray) , boost_foreach_argument_dependent_lookup_hack_value)))))) {} else for (bool _foreach_continue66 = true; _foreach_continue66 && !boost::foreach_detail_::done( _foreach_cur66 , _foreach_end66 , (true ? 0 : boost::foreach_detail_::encode_type(objArray, boost::foreach_detail_::is_const_(objArray)))); _foreach_continue66 ? boost::foreach_detail_::next( _foreach_cur66 , (true ? 0 : boost::foreach_detail_::encode_type(objArray, boost::foreach_detail_::is_const_(objArray)))) : (void)0) if (boost::foreach_detail_::set_false(_foreach_continue66)) {} else for (const TObject* s = boost::foreach_detail_::deref( _foreach_cur66 , (true ? 0 : boost::foreach_detail_::encode_type(objArray, boost::foreach_detail_::is_const_(objArray)))); !_foreach_continue66; _foreach_continue66 = true) {

    std::cout << s << '\n';

  }

}

The error itself happens for code like this:

int* f() {

  int a = 3;

  return &a;

}

int main() {

  const int*& rint = f();

}

I know this turns into an academic exercise. But I would like to solve the puzzle.

Thanks in advance,

Sebastian

Hi,

Did you try when implemented the range_begin and range_end that takes non const parameter?

Philippe.

Hi!

The last version I’ve posted uses range_begin/end( TObjArray & x ) The const functions are commented.

The error stays the same if I have both, the const and non-const functions.

Could it be a problem that there is no const version of TIter(Category). The example:

boost.org/doc/libs/1_39_0/do … ility.html

has std::string::const_iterator
in

inline std::string::const_iterator range_begin( sub_string const & x )

I’ve also tried “Making BOOST_FOREACH Work with Non-Copyable Sequence Types”. Error is the same. However a pointer to TObject is certainly copyable.

Thanks,

Sebastian

Hi,

[quote]Could it be a problem that there is no const version of TIter(Category). The example:
[/quote]It is plausible. Alternatively, it look like one need to find a way to make foreach_reference<T, C>::typebe “TObject*&” rather than a const.

Cheers,
Philippe.

Hi!

I ended up using the R__FOR_EACH macro defined in TCollection.h.

Cheers,

Sebastian

Perhaps you’re still interested in this, even though it’s been ten months since your post. I think the basic impediment to getting BOOST_FOREACH to work with a TObjArray is that dereferencing a TIterator returns a TObject * and not a TObject *&. The type detection machinery in BOOST_FOREACH works fine with standard-conforming iterators. I’m no C++ language lawyer, but TIterators don’t seem to be standard-conforming. The C++98 standard (24.1.3) says that the result of *a, where a is an iterator, should be T&.

It’s not an ideal solution, but you can get things to work by crafting a specialization of the “deref” function used by BOOST_FOREACH. You need to get it to do the impedance matching between what TIterator provides and what BOOST_FOREACH is expecting. If you add this to your example

namespace boost
{
  namespace foreach_detail_
  {
    template<>
    foreach_reference<TObjArray, mpl_::bool_<false> >::type
    deref(auto_any_t cur, type2type<TObjArray, mpl_::bool_<false> >*)
    {
      TIterCategory<TObjArray> it = auto_any_cast<foreach_iterator<TObjArray,
	mpl_::bool_<false> >::type, boost::mpl::false_>(cur);
      const TObject* p = *it;
      const TObject *&q = reinterpret_cast<const TObject *&>(p);
      return q;
    }
  }
}

which isn’t very pretty, I admit, then your code compiles and runs. For me. Today.

It would be cleaner and easier if dereferencing a TIterator just returned TObject *&.