Click here to Skip to main content
6,595,854 members and growing! (18,755 online)
Email Password   helpLost your password?
Languages » C / C++ Language » General     Intermediate

High Performance Dynamic Typing

By Christopher Diggins

A high-performance alternative to boost::any.
C++, Windows, Visual Studio, Dev
Posted:8 Aug 2005
Views:58,309
Bookmarked:37 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
21 votes for this article.
Popularity: 6.19 Rating: 4.68 out of 5
1 vote, 4.8%
1

2

3
2 votes, 9.5%
4
18 votes, 85.7%
5

Introduction

The Boost library provides a very useful little class called boost::any which can contain a value of virtually any type as long as that value supports copy construction and assignment. This boost::any type allows dynamic querying of the contained type, and safe type conversions. Despite its usefulness, boost::any is nowhere near as efficient as it could be, so I have rewritten my own, which I have immodestly called cdiggins::any.

Using cdiggins::any

The cdiggins::any type can be used to hold normal value types, and provides a mechanism to safely explicitly cast back to the appropriate type.

  cdiggins::any a = 42;
  int n = a.cast<int>();

If you try to cast to the wrong type an exception will be thrown:

  cdiggins::any a = 3.14;
  try {
    int n = a.cast<int>();
    cout << "should never be reached" << endl;
  }
  except(...) {
    cout << "just what I expected, an exception" << endl;
  }

You can use the cdiggins::any type to hold your own types if they support assignment and copy construction:

  struct fubar {
    int fu;
    int bar;
  };

  a = fubar();

You can extract type information at run-time using the type() member function. This returns a type_info struct describing the type of value being held:

  cout << a.type().name() << endl

Performance of cdiggins::any versus boost::any

The major drawback of the boost::any type is that it is quite slow because it indiscriminately uses dynamic allocations. The other drawback is that for every value type held there is the requirement for two extra pointers, one for a virtual function table and another for a pointer to the value. The cdiggins::any exhibits a very significant performance benefit (up to 40x in some cases) over boost::any.

There are several optimizations I used in the development of cdiggins::any in order to achieve these results:

  • there is only one extra pointer needed per instantiation of an any type.
  • types which have a size equal to or less than a void* do not undergo dynamic allocations when assigning a new value to an any which has the same underlying type, no dynamic reallocation occurs.

The Implementation

Finally without further ado, here is the implementation of the cdiggins::any class for your enjoyment:

/*
 * (C) Copyright Christopher Diggins 2005
 * (C) Copyright Pablo Aguilar 2005
 * (C) Copyright Kevlin Henney 2001
 *
 * Distributed under the Boost Software License, Version 1.0. (See
 * accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 */

#ifndef CDIGGINS_ANY_HPP
#define CDIGGINS_ANY_HPP

#include <stdexcept>

#include <typeinfo>

#include <algorithm>


namespace cdiggins
{
  struct bad_any_cast : std::bad_cast {
    bad_any_cast(const std::type_info& src, const std::type_info& dest)
      : from(src.name()), to(dest.name())
    { }
    virtual const char* what() {
      return "bad cast";
    }
    const char* from;
    const char* to;
  };

  namespace any_detail {
    // function pointer table

    struct fxn_ptr_table {
      const std::type_info& (*get_type)();
      void (*static_delete)(void**);
      void (*clone)(void* const*, void**);
      void (*move)(void* const*,void**);
    };

    // static functions for small value-types

    template<bool is_small>
    struct fxns
    {
      template<typename T>
      struct type {
        static const std::type_info& get_type() {
          return typeid(T);
        }
        static void static_delete(void** x) {
          reinterpret_cast<T*>(x)->~T();
        }
        static void clone(void* const* src, void** dest) {
          new(dest) T(*reinterpret_cast<T const*>(src));
        }
        static void move(void* const* src, void** dest) {
          reinterpret_cast<T*>(dest)->~T();
          *reinterpret_cast<T*>(dest) = *reinterpret_cast<T const*>(src);
         }
      };
    };

    // static functions for big value-types (bigger than a void*)

    template<>
    struct fxns<false>
    {
      template<typename T>
      struct type {
        static const std::type_info& get_type() {
          return typeid(T);
        }
        static void static_delete(void** x) {
          delete(*reinterpret_cast<T**>(x));
        }
        static void clone(void* const* src, void** dest) {
          *dest = new T(**reinterpret_cast<T* const*>(src));
        }
        static void move(void* const* src, void** dest) {
          (*reinterpret_cast<T**>(dest))->~T();
          **reinterpret_cast<T**>(dest) = **reinterpret_cast<T* const*>(src);
        }
      };
    };

    template<typename T>
    struct get_table
    {
      static const bool is_small = sizeof(T) <= sizeof(void*);

      static fxn_ptr_table* get()
      {
        static fxn_ptr_table static_table = {
          fxns<is_small>::template type<T>::get_type
        , fxns<is_small>::template type<T>::static_delete
        , fxns<is_small>::template type<T>::clone
        , fxns<is_small>::template type<T>::move
        };
        return &static_table;
      }
    };

    struct empty {
    };
  } // namespace any_detail


  struct any
  {
    // structors

    template <typename T>
    any(const T& x) {
      table = any_detail::get_table<T>::get();
      if (sizeof(T) <= sizeof(void*)) {
        new(&object) T(x);
      }
      else {
        object = new T(x);
      }
    }

    any() {
      table = any_detail::get_table<any_detail::empty>::get();
      object = NULL;
    }

    any(const any& x) {
      table = any_detail::get_table<any_detail::empty>::get();
      assign(x);
    }

    ~any() {
      table->static_delete(&object);
    }

    // assignment

    any& assign(const any& x) {
      // are we copying between the same type?

      if (table == x.table) {
        // if so, we can avoid reallocation

        table->move(&x.object, &object);
      }
      else {
        reset();
        x.table->clone(&x.object, &object);
        table = x.table;
      }
      return *this;
    }

    template <typename T>
    any& assign(const T& x)
    {
      // are we copying between the same type?

      any_detail::fxn_ptr_table* x_table = any_detail::get_table<T>::get();
      if (table == x_table) {
        // if so, we can avoid deallocating and resuse memory

        if (sizeof(T) <= sizeof(void*)) {
          // create copy on-top of object pointer itself

          new(&object) T(x);
        }
        else {
          // create copy on-top of old version

          new(object) T(x);
        }
      }
      else {
        reset();
        if (sizeof(T) <= sizeof(void*)) {
          // create copy on-top of object pointer itself

          new(&object) T(x);
          // update table pointer

          table = x_table;
        }
        else {
          object = new T(x);
          table = x_table;
        }
      }
      return *this;
    }

    // assignment operator

    template<typename T>
    any& operator=(T const& x) {
      return assign(x);
    }

    // utility functions

    any& swap(any& x) {
      std::swap(table, x.table);
      std::swap(object, x.object);
    }

    const std::type_info& get_type() const {
      return table->get_type();
    }

    template<typename T>
    const T& cast() const {
      if (get_type() != typeid(T)) {
        throw bad_any_cast(get_type(), typeid(T));
      }
      if (sizeof(T) <= sizeof(void*)) {
        return *reinterpret_cast<T const*>(&object);
      }
      else {
        return *reinterpret_cast<T const*>(object);
      }
    }

  // implicit casting is disabled by default 

  #ifdef ANY_IMPLICIT_CASTING
    // automatic casting operator

    template<typename T>
    operator T() const {
      return cast<T>();
    }
  #endif // implicit casting


    bool empty() const {
      return table == any_detail::get_table<any_detail::empty>::get();
    }

    void reset()
    {
      if (empty()) return;
      table->static_delete(&object);
      table = any_detail::get_table<any_detail::empty>::get();
      object = NULL;
    }

    // fields

    any_detail::fxn_ptr_table* table;
    void* object;
  };

  // boost::any-like casting

  template<typename T>
  T* any_cast(any* this_) {
    if (this_->get_type() != typeid(T)) {
      throw bad_any_cast(this_->get_type(), typeid(T));
    }
    if (sizeof(T) <= sizeof(void*)) {
      return reinterpret_cast<T*>(&this_->object);
    }
    else {
      return reinterpret_cast<T*>(this_->object);
    }
  }

  template<typename T>
  T const* any_cast(any const* this_) {
    return any_cast<T>(const_cast<any*>(this_));
  }

  template<typename T>
  T const& any_cast(any const& this_){
    return *any_cast<T>(const_cast<any*>(&this_));
  }
}

#endif // CDIGGINS_ANY_HPP

Under the Hood

This is a pretty wild class, but the idea is quite simple. A cdiggins::any instance contains two pointers: one points to a static table of function pointers, while the other points to a copy of the value. But here is the tricky trick I used to get fast performance for small types: if the value being held can fit inside of a void*, I don't actually bother allocating a new object, I force it into the pointer itself using placement new.

The only other somewhat tricky thing I do is create a set of static functions at compile-time for the type being contained. This is done through a call to the static template function get_table(). Using template specialization techniques, I am able to generate a set of function calls based on whether or not the type passed fits inside of a void pointer or not.

Final Words

Special thanks to Pablo Aguilar who pushed the code to new limits of safety and compiler compatibility. Pablo will be hopefully actively making more changes and making an official submission of the type to Boost. Best of luck Pablo!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Christopher Diggins


Member
  I've been programming personal computers since my I got my first Atari 400 in 1980. I am currently self-employed as a consultant and author.

  I've worked as a professional programmer for over 12 years. I have written for Doctor Dobbs Journal, and C++ Users Journal. I also contributed to the C++ Cookbook released by O'Reilly.

  My area of research is the design and implementation of programming languages. My current project is the Cat programming language which is a cross between Forth and Haskell.
Occupation: Web Developer
Location: Canada Canada

Other popular C / C++ Language articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 56 (Total in Forum: 56) (Refresh)FirstPrevNext
GeneralPreventing Reallocations PinmemberAnand Krishnamoorthi7:15 18 Apr '07  
QuestionSmall Types Assignment Problem? PinmemberAnand Krishnamoorthi7:10 18 Apr '07  
AnswerRe: Small Types Assignment Problem? Pinmemberxryl6693:28 5 Sep '07  
GeneralCompile problem with cast PinmemberNeville Franks14:15 17 Jul '06  
GeneralRe: Compile problem with cast Pinmemberpablo_mag15:00 17 Jul '06  
GeneralRe: Compile problem with cast [modified] PinmemberNeville Franks15:31 17 Jul '06  
GeneralRe: Compile problem with cast [modified] Pinmemberpablo_mag16:55 17 Jul '06  
GeneralRe: Compile problem with cast PinmemberNeville Franks19:53 17 Jul '06  
GeneralCompile error with const char* Pinmemberfarshizzo8:23 26 Apr '06  
GeneralRe: Compile error with const char* Pinmemberpablo_mag14:47 17 Jul '06  
GeneralUpdate Posted Pinmemberpablo_mag23:06 18 Sep '05  
GeneralRe: Update Posted PinmemberChristopher Diggins4:58 19 Sep '05  
GeneralRe: Update Posted PinmemberDavid O'Neil8:31 21 Sep '05  
GeneralRe: Update Posted Pinmemberpablo_mag8:33 21 Sep '05  
GeneralRe: Update Posted PinmemberTemp837:03 16 Feb '06  
GeneralRe: Update Posted Pinmemberpablo_mag7:18 16 Feb '06  
GeneralRe: Update Posted PinmemberTemp833:34 17 Feb '06  
GeneralRe: Update Posted Pinmemberpablo_mag12:36 17 Feb '06  
GeneralRe: Update Posted PinmemberTemp837:26 18 Feb '06  
GeneralRe: Update Posted Pinmemberpablo_mag11:31 18 Feb '06  
GeneralRe: Update Posted PinmemberTemp838:07 21 Feb '06  
GeneralRe: Update Posted Pinmemberpablo_mag8:14 21 Feb '06  
GeneralTrouble with classes with 'any' members used in vectors PinmemberDavid O'Neil8:42 12 Sep '05  
GeneralRe: Trouble with classes with 'any' members used in vectors Pinmemberpablo_mag13:05 16 Sep '05  
GeneralRe: Trouble with classes with 'any' members used in vectors PinmemberDavid O'Neil15:48 16 Sep '05  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 8 Aug 2005
Editor: Smitha Vijayan
Copyright 2005 by Christopher Diggins
Everything else Copyright © CodeProject, 1999-2009
Web17 | Advertise on the Code Project