LinuxQuestions.org
Share your knowledge at the LQ Wiki.
Go Back   LinuxQuestions.org > Blogs > rainbowsally
User Name
Password

Notices

Rate this Entry

An STL Template Experiment

Posted 12-18-2012 at 05:05 PM by rainbowsally

Today's features:
  • Getting an address of an iterator from stl.
  • Creating a foreach() macro.
  • A couple of ways to work around unwanted C++ compiler warnings.

STL is a freaking nighmare to trace in a debugger so it's not too easy to tell what goes on in them but one thing became quite clear and that is that iterators are generic pointer types internally, which makes sense since STL needs to deal with so many different types.

Here's a little test to play with to show how the 'iterators' are in fact easily decyphered.

file: src/vector-test.cpp
purpose: test casting iterators to other kinds of pointers.
Code:
// vector-test.cpp

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>

#include <vector>
#include <iostream>
#include <stdio.h>

using namespace std;

void dbg(){} // for a non-moving breakpoint

int main(int argc, char** argv)
{
  dbg();
  vector<int> test;
  test.push_back(123);
  vector<int>::iterator a[2];
  int** piters;
  
  if(sizeof(a) == 2 * sizeof(void*)) // which it always will
  {
    // these pointers may change any time another item is
    // added to the vector.
    
    a[0] = test.begin();
    a[1] = test.end();
    piters = (int**)a; //itersarray;
  }
  else
    printf("WOOPSIE!\n"), exit(1);
  
#warning The following warnings are caused by type mismatches.
  printf("Using template defs\n");  
  // this next line generates a warning.  We are printing the address of
  // the iterator.
  printf("begin()=%lx end()=%lx\n",test.begin(),test.end());
  printf("*begin=%d *end=%d\n\n",*test.begin(),*test.end());
  
#warning The following code gets the same values without warnings.
  printf("Using known sizes and pointers\n");
  // this way the warning is shut off and we can cast the iterator to 
  // a void* (which it is internally) and see it's address.
  printf("begin()=%lx end()=%lx\n",(ulong)piters[0],(ulong)piters[1]);
  printf("*begin=%d *end=%d\n\n",*(piters[0]),*(piters[1]));
  
  return 0;
}
[Note for mc2 users: To change an mc2.def to a MULTI type (one output file per source file in SRCDIR) just change OUTNAME = MULTI, make clean, make update, and make all. Also, to disable the format warnings add -Wno-format to the CFLAGS.]

If you've never tried to 'cast' these iterator things to other types, you might be surprised how difficult that can be. ;-)

But now, having accomplised the impossible, we can see what's going on. The iterators in this case are contiguous addresses in a list that grows (doubling in size) whenever it outgrows it's previous capacity.

[But iterators may not always be in a contiguous memory area. They could be linked lists, so the only way to be sure not to take a dump is to use the iterators, ugly as they may be.]

Iterator syntax is particularly ugly so to create a perl-like "foreach()" function that acts more like like a simple for(..) loop declaration try this (with the same includes as above)

Code:
// The class that deals with the foreach() variables.
template <typename T>
class ForIter {
public:
  const T _t;       // type
  int _f;           // break flag for outer loop
  typename T::const_iterator _i, end;
  inline ForIter(const T& t) 
  : _t(t), _f(0), _i(_t.begin()), end(_t.end()) { }
};

// Syntactical interface
#define foreach(name, classT) \
  for( \
       ForIter<__typeof__(classT)> _classT_(classT); \
      !_classT_._f && _classT_._i != _classT_.end; \
        __extension__ ({ ++_classT_._f; ++_classT_._i; }) \
      ) \
      for( \
          name = *_classT_._i; ; \
        __extension__ ({--_classT_._f; break;}) \
         )

int main(int argc, char** argv)
{
  dbg();
  
  vector<int> test;
  test.push_back(123);
  test.push_back(456);
  test.push_back(789);
  foreach(int it, test)
  {
    printf("%d\n", it);
  }
  return 0;
}
A bit of explanation may be in order here. :-)

The __extension__ compiler directive turns off certain warnings and if you have a good syntax highlighter, it will turn off the syntax error flag when you use this macro.

The oddball section of code is that part that's within the third field of the "for()" loops which execute several statements within braces which are within parens, but are in fact simply what is done after execution of each loop.

This is a very nice foreach() macro, giving meaningful error messages when the syntax is screwy. It will 'break' a loop properly due to the use of the flag and this is a very interesting way to execute code that follows a macro definition. See if you can figure out why there are two for()'s but only one loop.

Here's the output if it works.

Code:
$> src/foreach-test # (the name of my test file)
123
456
789
See GPL version 3 (especially around line 50, which incidentally, is why I may NEVER use another open??SUSE Linux again if they don't clean up their source code repositories).

- The Computer Mad Science Team

:-)
Posted in Uncategorized
Views 494 Comments 0
« Prev     Main     Next »
Total Comments 0

Comments

 

  



All times are GMT -5. The time now is 01:53 AM.

Main Menu
Advertisement

My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration