Boost::Any ported to D

Marcin Kuszczak aarti at interia.pl
Sun Sep 24 15:18:02 PDT 2006


Hello all!

I am happy to announce that Boost::any is now ported to D Programming
Language. 

You can find sources in attachment. You can find there also original boost
file for reference.

Documentation is on boost site: http://boost.org/doc/html/any.html - with
below modifications:
1. You can not construct Any type with specific value as parameter of
constructor.
2. There is no assignment operator in D, so I used function template
function assign()

Test program attached.

After translation I have to say that D is great - source is about half size
of original and is much more understantable (I hope) <g>.

I encountered also few problems when porting from C++. Some of them are
probably bugs, some, maybe are D Language design decisions - I would be
happy to know how to threat them.

Here are they:
1. I couldn't make templatized class constructor
        this(ValueType)(ValueType v);
   It seems that it shouldn't be a problem to support such a construct in 
   language - constructor is very similar to method which can be 
   templatized.
2. Arrays with static size are not fully supported e.g. function can not
return static array. Also static arrays are not implicitly converted to
dynamic arrays, so when assigning string to Any class there is a need to
cast to dynamic array:
        (new Any).assign(cast(char[])("char[] test"));
3. I can not compile program after putting PlaceHolder interface and Holder
class into Any class (like in the original approach). Is it bug or am I
doing something wrong?
4. Why specialization for implicitly instatiated function templates is not
possible? (But it is possible to use static if and is operator).
5. It seems that compiler works different when calling it in different way -
at least on Linux.
For example below commands are not equivalent:
dmd test_any.d any.d
above command works

dmd -c -I../../ test_any.d
dmd -c any.d
gcc test_any.o any.o -o test_any -m32 -lphobos -lpthread -lm -Xlinker -ldl
above command produces error message:
test_any.o: In function
`_D5doost3any3any28__T7anyCastTC8test_any4TestZ7anyCastFC5doost3any3any3AnyZC8test_any4Test':test_any.d
(.gnu.linkonce.t_D5doost3any3any28__T7anyCastTC8test_any4TestZ7anyCastFC5doost3any3any3AnyZC8test_any4Test+0x54):
undefined reference to `_init_24TypeInfo_C8test_any4Test'
test_any.o: In function
`_D5doost3any3any27__T6HolderTC8test_any4TestZ6Holder4typeFZC8TypeInfo':test_any.d
(.gnu.linkonce.t_D5doost3any3any27__T6HolderTC8test_any4TestZ6Holder4typeFZC8TypeInfo+0x9):
undefined reference to `_init_24TypeInfo_C8test_any4Test'
collect2: ld returned 1 exit status

It seems that similar situation is when using Box from std.boxer. It is
especially painful as very good IDE Codeblocks uses this method of
compilation of D programs.

I used namespace doost (read: dust :-)) as I think it would be good idea to
put all classes translated from boost to one library. (I am also in
progress of translating boost::program_options, which will also be putted
into doost namespace).

Review of code and comments are welcomed.

-- 
Regards
Marcin Kuszczak
(Aarti_pl)

--nextPart4266616.56G3oQPyTi
Content-Type: text/x-c++src; name="any.d"
Content-Transfer-Encoding: 8Bit
Content-Disposition: attachment; filename="any.d"

// Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved.
//
// 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)

// See http://www.boost.org/libs/Any for Documentation.
// what:  variant type boost::Any
// who:   contributed by Kevlin Henney,
//        with features contributed and bugs found by
//        Ed Brey, Mark Rodgers, Peter Dimov, and James Curran
// when:  July 2001
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
// Translation to D Programming Language
// (c) Marcin Kuszczak, 2006
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

module doost.any.any;

// -----------------------------------------------------------------------------

interface PlaceHolder {
    TypeInfo type();
    PlaceHolder clone();
}

// -----------------------------------------------------------------------------

class Holder(ValueType) : PlaceHolder {
public:
    this(ValueType value) {
        held=value;
    }

    TypeInfo type() {
        return typeid(ValueType);
    }

    PlaceHolder clone() {
        return new Holder(held);
    }

    ValueType held;
}

// -----------------------------------------------------------------------------

class Any {
public:
    this() {
        content=null;
    }

    Any assign(ValueType)(ValueType value) {
        static if (is(ValueType == Any)) content = value.content!=null ? value.content.clone() : null;
            else content=new Holder!(ValueType)(value);
        return this;
    }

    Any swap(Any rhs) {
        PlaceHolder p=content;
        content=rhs.content;
        rhs.content=p;
        return this;
    }

    bool empty() {
        return (content is null);
    }

    TypeInfo type() {
        return content!=null ? content.type() : typeid(void);
    }

private:
    PlaceHolder content;
}

// -----------------------------------------------------------------------------

ValueType anyCast(ValueType)(Any operand) {
    if (operand is null) throw new BadAnyCast(BadAnyCast.NullOperand);
    if (operand.empty) throw new BadAnyCast(BadAnyCast.ValueNotAssigned);
    if (operand.type!=typeid(ValueType)) throw new BadAnyCast(BadAnyCast.InvalidType);

    auto p=cast(Holder!(ValueType))(operand.content);
    return p.held;
}

// -----------------------------------------------------------------------------

class BadAnyCast : Exception {
public:
    enum {NullOperand, ValueNotAssigned, InvalidType};

    this(int reason) {
        char[] msg="BadAnyCast: failed conversion using anyCast";

        switch (reason) {
            case NullOperand : msg~= " - operand is null"; break;
            case ValueNotAssigned: msg~= " - value is not assigned"; break;
            case InvalidType: msg~= " - type of value is different than requested"; break;
            default: break;
        }

        super(msg);
    }

    int reason;
}

--nextPart4266616.56G3oQPyTi
Content-Type: text/x-c++src; name="any.hpp"
Content-Transfer-Encoding: 8Bit
Content-Disposition: attachment; filename="any.hpp"

// See http://www.boost.org/libs/any for Documentation.

#ifndef BOOST_ANY_INCLUDED
#define BOOST_ANY_INCLUDED

// what:  variant type boost::any
// who:   contributed by Kevlin Henney,
//        with features contributed and bugs found by
//        Ed Brey, Mark Rodgers, Peter Dimov, and James Curran
// when:  July 2001
// where: tested with BCC 5.5, MSVC 6.0, and g++ 2.95

#include <algorithm>
#include <typeinfo>

#include "boost/config.hpp"
#include <boost/type_traits/remove_reference.hpp>
#include <boost/type_traits/is_reference.hpp>
#include <boost/throw_exception.hpp>
#include <boost/static_assert.hpp>

namespace boost
{
    class any
    {
    public: // structors

        any()
          : content(0)
        {
        }

        template<typename ValueType>
        any(const ValueType & value)
          : content(new holder<ValueType>(value))
        {
        }

        any(const any & other)
          : content(other.content ? other.content->clone() : 0)
        {
        }

        ~any()
        {
            delete content;
        }

    public: // modifiers

        any & swap(any & rhs)
        {
            std::swap(content, rhs.content);
            return *this;
        }

        template<typename ValueType>
        any & operator=(const ValueType & rhs)
        {
            any(rhs).swap(*this);
            return *this;
        }

        any & operator=(const any & rhs)
        {
            any(rhs).swap(*this);
            return *this;
        }

    public: // queries

        bool empty() const
        {
            return !content;
        }

        const std::type_info & type() const
        {
            return content ? content->type() : typeid(void);
        }

#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
    private: // types
#else
    public: // types (public so any_cast can be non-friend)
#endif

        class placeholder
        {
        public: // structors
    
            virtual ~placeholder()
            {
            }

        public: // queries

            virtual const std::type_info & type() const = 0;

            virtual placeholder * clone() const = 0;
    
        };

        template<typename ValueType>
        class holder : public placeholder
        {
        public: // structors

            holder(const ValueType & value)
              : held(value)
            {
            }

        public: // queries

            virtual const std::type_info & type() const
            {
                return typeid(ValueType);
            }

            virtual placeholder * clone() const
            {
                return new holder(held);
            }

        public: // representation

            ValueType held;

        };

#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS

    private: // representation

        template<typename ValueType>
        friend ValueType * any_cast(any *);

#else

    public: // representation (public so any_cast can be non-friend)

#endif

        placeholder * content;

    };

    class bad_any_cast : public std::bad_cast
    {
    public:
        virtual const char * what() const throw()
        {
            return "boost::bad_any_cast: "
                   "failed conversion using boost::any_cast";
        }
    };

    template<typename ValueType>
    ValueType * any_cast(any * operand)
    {
        return operand && operand->type() == typeid(ValueType)
                    ? &static_cast<any::holder<ValueType> *>(operand->content)->held
                    : 0;
    }

    template<typename ValueType>
    const ValueType * any_cast(const any * operand)
    {
        return any_cast<ValueType>(const_cast<any *>(operand));
    }

    template<typename ValueType>
    ValueType any_cast(const any & operand)
    {
        typedef BOOST_DEDUCED_TYPENAME remove_reference<ValueType>::type nonref;

#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
        // If 'nonref' is still reference type, it means the user has not
        // specialized 'remove_reference'.

        // Please use BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION macro
        // to generate specialization of remove_reference for your class
        // See type traits library documentation for details
        BOOST_STATIC_ASSERT(!is_reference<nonref>::value);
#endif

        const nonref * result = any_cast<nonref>(&operand);
        if(!result)
            boost::throw_exception(bad_any_cast());
        return *result;
    }

    template<typename ValueType>
    ValueType any_cast(any & operand)
    {
        typedef BOOST_DEDUCED_TYPENAME remove_reference<ValueType>::type nonref;

#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
        // The comment in the above version of 'any_cast' explains when this
        // assert is fired and what to do.
        BOOST_STATIC_ASSERT(!is_reference<nonref>::value);
#endif

        nonref * result = any_cast<nonref>(&operand);
        if(!result)
            boost::throw_exception(bad_any_cast());
        return *result;
    }


}

// Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved.
//
// 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)

#endif

--nextPart4266616.56G3oQPyTi
Content-Type: text/x-java; name="test_any.d"
Content-Transfer-Encoding: 8Bit
Content-Disposition: attachment; filename="test_any.d"

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Example program to Any (discriminated variant container)
//
// Translation to D Programming Language
// (c) Marcin Kuszczak, 2006
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

import doost.any.any;
import std.stdio;

// -----------------------------------------------------------------------------

Any[] many;

// -----------------------------------------------------------------------------

class Test {
    char[] toString() {
        return "Test class";
    }
}

void printVector() {
    // type, empty and anyCast test

    for(uint i=0; i<many.length; i++) {
        writef("\tNo. %s; ", i);
        writef("\tType: %s; ", many[i].type);
        writef("\tEmpty: %s; ", many[i].empty);
        if (many[i].empty==false) {
            writef("\tValue: ");
            switch (many[i].type.toString) {
                case "int" : writef(anyCast!(int)(many[i])); break;
                case "double" : writef(anyCast!(double)(many[i])); break;
                case "char[]" : writef(anyCast!(char[])(many[i])); break;
                case "Test" : writef(anyCast!(Test)(many[i]).toString); break;
                default: writef("Unknown type - don't know how to cast");
            }
        }
        writefln;
    }
}

// -----------------------------------------------------------------------------

void main() {

    //Assigning test
    writef("Assigning test");
    writefln();
    many~=(new Any).assign(5);
    many~=(new Any).assign(5.5);
    many~=(new Any).assign(cast(char[])("char[] test"));
    many~=(new Any).assign(new Test);
    many~=(new Any).assign(many[3]);
    many~=new Any;
    many~=(new Any).assign(many[0]);

    printVector();
    writefln();

    // exception handling test
    writef("Exception handling test");
    writefln();
    writef("Casting null to int: ");
    try {
        anyCast!(int)(null);
    }
    catch (BadAnyCast e) {
        writefln(e.toString);
    }

    writef("Casting empty Any to int: ");
    try {
        anyCast!(int)(many[5]);
    }
    catch (BadAnyCast e) {
        writefln(e.toString);
    }

    writef("Casting Test to char[]: ");
    try {
        anyCast!(char[])(many[3]);
    }
    catch (BadAnyCast e) {
        writefln(e.toString);
    }

    writefln();

    // swaping test
    writef("Swaping test");
    writefln();
    many[0].swap(many[3]);
    many[5].swap(many[2]);
    printVector;
}


More information about the Digitalmars-d-announce mailing list