[C++-sig] map key

David Abrahams dave at boost-consulting.com
Fri Jan 17 23:29:28 CET 2003


"Mike Rovner" <mike at bindkey.com> writes:

> I'm trying to wrap a std::map<const Key*, Value>  with
>
> template<class T>
> struct map_item
>
> {
>
> typedef typename T::key_type Key;

<snip>

Mike, lots of mailers don't respond well to indentation with tabs.

Cleaning up your code, I get:

template<class T>
struct map_item
{
    typedef typename T::key_type Key;
    typedef typename T::mapped_type Value;

    static Value const& get(T& x, const Key n)
    {
        if( x.find(n) != x.end() ) return x[n];
        PyErr_SetString(PyExc_KeyError,"Map key not found");
        throw_error_already_set();
    }

    static void set(T& x, const Key n, const Value& val) { x[n]=val; }

    static void del(T& x, const Key n) { x.erase(n); }

    static bool in(T const& x, const Key n) { return x.find(n) != x.end(); }

    static list keys(T const& x)
    {
        list t;
        for(T::const_iterator it=x.begin(); it!=x.end(); ++it)
            t.append( object(it->first) );
You don't need this---^^^^^^^---------^
        return t;
    }

    static list values(T const& x)
    {
        list t;
        for(T::const_iterator it=x.begin(); it!=x.end(); ++it)
            t.append( object(it->second) );
You don't need this---^^^^^^^----------^
        return t;
    }
    static list items(T const& x)
    {
        list t;
        for(T::const_iterator it=x.begin(); it!=x.end(); ++it)
            t.append( make_tuple(object(it->first), object(it->second)) );
You don't need this--------------^^^^^^^---------^--^^^^^^^----------^
        return t;
    }
};

typedef std::map<const Key*, Value> Map;
class_<Key, boost::noncopyable>("Key", no_init) /*...*/;
class_<Value>("Value") /*...*/;

class_<Map>("Map")
    .def("keys", &map_item<Map>::keys)
    ;
--

> My problem is to return keys:
>
>>>> m=Map(...)
>>>> m.keys()
>
> TypeError: No to_python (by-value) converter found for C++ type:
> class Key
>
> Other functions returning const Key* are just fine with
> return_internal_reference.

There is no function returning const Key* being invoked, but I see
what you mean.  The problem occurs where you try to convert a ``const
Key*'' to a Python object.

> Can I specify return_internal_reference when constructing keys list? 

Hum, that's an interesting question.  I guess you'd like each of the
items in the keys list to keep the map object alive so that they will
remain valid.  You could use t.append(ptr(it->second)), which would
build an object around the pointer and stick it in the list, but that
would not have the desired lifetime-management properties.

One approach would be to wrap a function which could convert
the pointer with the lifetime management you want:

    Key const* managed(object map, Key const* x) { return x; }
    object py_managed = make_function(managed, return_internal_reference<>());

Then you need to get ahold of the map's Python object in your keys
function:

    static list keys(python::back_reference<T const&> xx)
    {
        T const& x = xx.get();
        list t;
        for(T::const_iterator it=x.begin(); it!=x.end(); ++it)
            t.append( py_managed(xx.source(), it->first) );
        return t;
    }

> How?  Will it solve the problem?

I think that would solve it.

Mike, I think this would make a really good tutorial case study for
the Boost.Python documentation.  Is there any way I could persuade you
to write it up so that others can benefit?

-- 
                       David Abrahams
   dave at boost-consulting.com * http://www.boost-consulting.com
Boost support, enhancements, training, and commercial distribution





More information about the Cplusplus-sig mailing list