Writing Portable Code in C++'�A Vectors and String Issue

Environment: C++

Writing portable code is an interesting as well as a challenging task sometimes. You have to restrict yourself not only to the standard of the language but sometimes also to the implementation of the language and its libraries on different platforms. Sometimes the standard of language doesn't say anything about the implementation of some features and lets the vendor implement it in their own way.

One problem may arise during the writing of code that runs on multiple platform is the case sensitivity of the file system. Windows preserves the case of files, but its file system is not case sensitive; however, the Unix file system is case sensitive. On Unix, "Bounds.h" and "bounds.h" are two different files. So, be careful to include files in the proper case if you want to run your code on different platforms.

Sometimes, there may be interesting situations created due to different implementations of a library. One such example is vector and string classes in the C++ Standard Template Library [JOS99]. Suppose you make an vector of 10 elements and shuffle it randomly by using the STL algorithm.

vector<int> vec;

for (int iIndex = 0; iIndex < 10; ++iIndex)
{
  vec.push_back(iIndex * iIndex);
}

random_shuffle(vec.begin(), vec.end());

After that, for any reason, you want to sort a range of elements, but not all elements. Suppose you do not want to sort the first and last elements. It will be something like this:

sort(vec.begin() + 1, vec.end() - 1);

Well, what about writing the same code something like this:

sort(++vec.begin(), --vec.end());

Isn't it equal to the previous line? Well, the behavior of this code depends on the implementation of the standard library you use with your compiler. This code may work fine according to your requirement—or it may give a compilation error. To be more specific, Visual C++ 7.0 compiles this code; Visual C++ 6.0 will give you a compilation error.

To see why this code is not portable, we have to look at the specification of a vector in the C++ standard and its implementation in different libraries. According to the C++ standard, a vector does have a random access iterator and stores its elements in a continuous memory location, just like an array. The natural solution that comes to mind to make an iterator of vectors is a pointer of the template type. All the specifications for the random access iterator would be accessed with this pointer. So, the code to make a vector class is something like what follows. Here, I use upper-case letters to distinguish it from standard libraries' names and omit unnecessary detail.

template <typename _Type>
class Allocator
{
public:
  typedef _Type* pointer;
  // other stuff
};

template <typename _Type, typename _Allocator = Allocator<_Type> >
class Vector
{
typedef typename _Allocator::pointer _Tptr;
  typedef _Tptr iterator;
  // other stuff

protected:
  iterator _First, _Last, _End;
};

To begin and end the member function of the vector, just return the pointer of the vector type.

template <typename _Type, typename _Allocator = Allocator<_Type> >
class Vector
{
  // other stuff
public:
  iterator begin() { return (_First); }

  iterator end()   { return (_Last); }

};

The ++ and -- operators on raw pointer return rvalue, so you can't assign another value to it. This is the reason you can't use a sort algorithm when a random access iterator is implemented as a raw pointer. The code explains this operation in a simplified way.

int arr[] = {1, 2, 3, 4, 5};

*(++arr) = 10;      // Cannot compile
*(arr + 1) = 10;    // Compile

However, some implementations wrap the pointer into a class. Here is the simplest implementation of this; again, extra detail is omitted.

template <typename _Type>
class Allocator
{
public:
  typedef _Type* pointer;
};
// wrap pointer into a class
template<class _Type>
class _Ptrit
{
public:
  // other functions
  _Type& operator++()
  {
    ++current;
    return (*this);
  }

  _Type operator++(int)
  {
    _Type _Tmp = *this;
    ++current;
    return (_Tmp);
  }

  _Type& operator--()
  {
    --current;
    return (*this);
  }

  _Type operator--(int)
  {
    _Type _Tmp = *this;
    --current;
    return (_Tmp);
  }

protected:
  _Type current;
};

template <typename _Type, typename _Allocator = Allocator<_Type> >
class Vector
{
  typedef _Allocator::pointer _Tptr;
  typedef _Ptrit<_Tptr> iterator;
  // other stuff

protected:
  iterator _First, _Last, _End;

public:
  // other functions
  iterator begin()
  {
    return (_First);
  }

  iterator end()
  {
    return (_Last);
  }
};

In this implementation, the above code will compile. The same situation may arise in the case of a string because some implementations use char* as a random access iterator.

string str("Hello World");

// may not compile on all implementations
sort(++str.begin(), --str.end());

// compile
sort(vec.begin() + 1, vec.end() - 1);

These are a few reasons why writing portable code is challenging as well as fun. So, be sure to check your code on different implementations on different platform if you want to make it portable, even if it is written according to a language standard, because different implementations may behave differently on your code although they follow the rules of a standard language.

See Writing Portable Code in C++—A Variable Scope Issue for more issues regarding the writing of portable code.

References

  1. [DEW02] C++ Gotchas, Avoiding Common Problems in Coding and Design. Stephen Dewhurst 2002
  2. [ISO98] International Standard Programming Language C++ ISO/ICE 14882 1998
  3. [JOS99] The C++ Standard Library: A Tutorial and Reference. Nicolai M. Josuttis 1999



About the Author

Zeeshan Amjad

C++ Developer at Bechtel Corporation. zamjad.wordpress.com

Comments

  • doubt

    Posted by Legacy on 12/15/2003 12:00am

    Originally posted by: Debug

    [quote]
    The ++ and -- operators on raw pointer return rvalue, so you can't assign another value to it. This is the reason you can't use a sort algorithm when a random access iterator is implemented as a raw pointer. The code explains this operation in a simplified way.int arr[] = {1, 2, 3, 4, 5};
    *(++arr) = 10; // Cannot compile
    *(arr + 1) = 10; // Compile
    [end quote]

    But
    int arr[]={ 1, 2, 3, 4, 5, 6 };
    int* ptr=arr;
    *(++ptr)=10; // compile & work
    Means this that ++ and -- operators does not return rvalue?

    Reply
  • Good Work!!!!

    Posted by Legacy on 12/13/2003 12:00am

    Originally posted by: Nagesh

    Hi Zeeshan
    Its nice article which focus some light of porting.
    Well...I found here you are mostly concetrating on the STL.
    Does this mean that only STL has to be has to be taken cared while porting. and other things will remain as per the ANSI standard.
    If we are using some O.S specific API,what should we do while porting. I mean is there any article which tells cross-platform API equvalant (functionality wise).
    ummmm....The link which will give us O.S wise comparison.

    I agree, this is out of scope of this article. But just I want to know about it.
    Thank you
    Regards
    Nagesh

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Hybrid cloud platforms need to think in terms of sweet spots when it comes to application platform interface (API) integration. Cloud Velocity has taken a unique approach to tight integration with the API sweet spot; enough to support the agility of physical and virtual apps, including multi-tier environments and databases, while reducing capital and operating costs. Read this case study to learn how a global-level Fortune 1000 company was able to deploy an entire 6+ TB Oracle eCommerce stack in Amazon Web …

  • Managing your company's financials is the backbone of your business and is vital to the long-term health and viability of your company. To continue applying the necessary financial rigor to support rapid growth, the accounting department needs the right tools to most efficiently do their job. Read this white paper to understand the 10 essentials of a complete financial management system and how the right solution can help you keep up with the rapidly changing business world.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds