Chad's Blog

STL collections with Java and SWIG

Posted in C++, Java, SWIG by chadretz on November 27, 2009

When using SWIG with Java, I quickly realized that there were wasn’t support for std::set or std::list and only minimal support for std::map and std::vector (i.e. no proper iteration). Other languages (python, ruby, etc) had std_set.i and std_list.i whereas Java does not. I am not a C++ expert and definitely not an expert at writing SWIG typemaps.

My only solution was to write C++ wrappers for these collections. For some I also needed iterator wrappers to support their Java counterparts. They are listed below (collapsed by default).

ListWrapper.h

#pragma once
#include <list>

template<class T>
class ListWrapper
{
public:
	std::list<T>* _list;
	ListWrapper(std::list<T>* original)
	{
		this->_list = original;
	}

	~ListWrapper()
	{
	}

	int size()
	{
		return this->_list->size();
	}

	bool contains(T item)
	{
		for(std::list<T>::iterator iter = this->_list->begin(); 
				iter != this->_list->end(); iter++) {
			if (*iter == item) {
				return true;
			}
		}
		return false;
	}

	bool add(T item)
	{
		this->_list->push_back(item);
		return true;
	}

	void clear()
	{
		this->_list->clear();
	}

	bool remove(T item)
	{
		int size = this->_list->size();
		this->_list->remove(item);
		return size != this->_list->size();
	}
};

template<class T>
class ListIterator
{
private:
	std::list<T>* _list;
	typename std::list<T>::const_iterator _iter;
public:
	ListIterator(std::list<T>* original)
	{
		this->_list = original;
		this->_iter = this->_list->begin();
	}

	bool hasNext()
	{
		return this->_iter != this->_list->end();
	}

	T next()
	{
		T ret = (T) *this->_iter;
		this->_iter++;
		return ret;
	}
};

MapWrapper.h

#pragma once
#include <map>

template<class K, class V>
class MapWrapper
{
private:
	std::map<K, V>* _map;
public:
	MapWrapper(std::map<K, V>* original)
	{
		this->_map = original;
	}

	~MapWrapper()
	{
	}

	int size()
	{
		return this->_map->size();
	}

	bool add(K key, V value)
	{
		bool present = this->_map->find(key) != this->_map->end();
		(*this->_map)[key] = value;
		return present;
	}

	void clear()
	{
		this->_map->clear();
	}

	bool remove(K key)
	{
		std::map<K, V>::const_iterator iter = this->_map->find(key);
		if (iter != this->_map->end()) {
			this->_map->erase(iter);
			return true;
		} else {
			return false;
		}
	}
};

template<class K, class V>
class MapIterator
{
private:
	std::map<K, V>* _map;
	typename std::map<K, V>::iterator _iter;
	std::pair<K, V> _current;
public:
	MapIterator(std::map<K, V>* original)
	{
		this->_map = original;
		this->_iter = this->_map->begin();
	}

	bool hasNext()
	{
		return this->_iter != this->_map->end();
	}

	void next()
	{
		this->_current = *this->_iter;
		this->_iter++;
	}

	K getKey()
	{
		return this->_current.first;
	}

	V getValue()
	{
		return this->_current.second;
	}
};

SetWrapper.h

#pragma once
#include <set>

template<class T>
class SetWrapper
{
private:
	std::set<T>* _set;
public:
	SetWrapper(std::set<T>* original)
	{
		this->_set = original;
	}

	~SetWrapper()
	{
	}

	int size()
	{
		return this->_set->size();
	}

	bool contains(T item)
	{
		std::set<T>::const_iterator iter = this->_set->find(item);
		return iter != this->_set->end();
	}

	bool add(T item)
	{
		return this->_set->insert(item).second;
	}

	void clear()
	{
		this->_set->clear();
	}

	bool remove(T item)
	{
		std::set<T>::const_iterator iter = this->_set->find(item);
		if (iter != this->_set->end()) {
			this->_set->erase(iter);
			return true;
		} else {
			return false;
		}
	}
};

template<class T>
class SetIterator
{
private:
	std::set<T>* _set;
	typename std::set<T>::iterator _iter;
public:
	SetIterator(std::set<T>* original)
	{
		this->_set = original;
		this->_iter = this->_set->begin();
	}

	bool hasNext()
	{
		return this->_iter != this->_set->end();
	}

	T next()
	{
		T ret = (T) *this->_iter;
		this->_iter++;
		return ret;
	}
};

VectorWrapper.h

#pragma once
#include <vector>

template<class T>
class VectorWrapper
{
private:
	std::vector<T>* _vector;
public:
	VectorWrapper(std::vector<T>* original)
	{
		this->_vector = original;
	}

	~VectorWrapper()
	{
	}

	int size()
	{
		return this->_vector->size();
	}

	void add(int index, T item)
	{
		this->_vector->insert(this->_vector->begin() + index, item);
	}

	void clear()
	{
		this->_vector->clear();
	}

	T set(int index, T item)
	{
		T ret = this->get(index);
		(*this->_vector)[index] = item;
		return ret;
	}

	T remove(int index)
	{
		T item = this->_vector->at(index);
		this->_vector->erase(this->_vector->begin() + index);
		return item;
	}

	T get(int index)
	{
		return this->_vector->at(index);
	}
};

Basically all these do is provide SWIG-readable methods for the underlying collections. Again, I am not a C++ expert and I only have minimal test cases to prove to me they work. These headers should be “%import”ed in your SWIG interface.

Now I need to wrap these in normal Java collections. I use std::map, std::set, and std::vector in Java as a Map, Set, and List respectively. In STL, std::list is not indexed and therefore must also be mapped to a Set. Note, this violates the Java contract of a Set where it is assumed that all elements are unique. I suppose I could I have used a Queue or an AbstractSequentialList, but I’ll stick w/ Set for now (iteration is still in order).

This means I needed 3 Java implementations: NativeList (for vectors), NativeMap (for maps), and NativeSet (for sets and lists). They are listed below (collapsed by default)

NativeList.java

package org.cretz.swig.collection;

import java.lang.reflect.Method;
import java.util.AbstractList;
import java.util.List;

/**
 * Wrapper for std::vector from SWIG
 * 
 * @author Chad Retz
 *
 * @param <T>
 */
public class NativeList<T> extends AbstractList<T> implements List<T> {

	private final Object listWrapper;
	private final Method sizeMethod;
	private final Method addMethod;
	private final Method clearMethod;
	private final Method setMethod;
	private final Method removeMethod;
	private final Method getMethod;
	
	/**
	 * Construct native list from std::vector wrappers
	 * 
	 * @param nativeClass The native class
	 * @param nativeList The SWIG vector
	 * @param listWrapperClass The SWIG vector class wrapper
	 */
	public NativeList(Class<T> nativeClass, Object nativeList, Class<?> listWrapperClass) {
		try {
			listWrapper = listWrapperClass.getConstructor(nativeList.getClass()).
					newInstance(nativeList);
			sizeMethod = listWrapperClass.getDeclaredMethod("size");
			addMethod = listWrapperClass.getDeclaredMethod("add", 
					Integer.TYPE, nativeClass);
			clearMethod = listWrapperClass.getDeclaredMethod("clear");
			setMethod = listWrapperClass.getDeclaredMethod("set", 
					Integer.TYPE, nativeClass);
			removeMethod = listWrapperClass.getDeclaredMethod("remove", Integer.TYPE);
			getMethod = listWrapperClass.getDeclaredMethod("get", Integer.TYPE);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	@Override
	public void add(int index, T item) {
		try {
			addMethod.invoke(listWrapper, index, item);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	@Override
	public void clear() {
		try {
			clearMethod.invoke(listWrapper);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	@Override
	@SuppressWarnings("unchecked")
	public T set(int index, T item) {
		try {
			return (T) setMethod.invoke(listWrapper, index, item);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}		
	}

	@Override
	@SuppressWarnings("unchecked")
	public T remove(int index) {
		try {
			return (T) removeMethod.invoke(listWrapper, index);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	@Override
	public boolean remove(Object item) {
		try {
			return (Boolean) removeMethod.invoke(listWrapper, item);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	@SuppressWarnings("unchecked")
	public T get(int index) {
		try {
			return (T) getMethod.invoke(listWrapper, index);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public int size() {
		try {
			return (Integer) sizeMethod.invoke(listWrapper);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}

NativeMap.java

package org.cretz.swig.collection;

import java.lang.reflect.Method;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Wrapper for std::map from SWIG. This is basically
 * the same as std::set and impl w/ {@link org.cretz.swig.collection.NativeSet}
 * 
 * @author Chad Retz
 *
 * @param <K>
 * @param <V>
 */
public class NativeMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {

	private final Class<?> mapIteratorClass;
	private final Object nativeMap;
	private final Object mapWrapper;
	private final Method sizeMethod;
	private final Method addMethod;
	private final Method clearMethod;
	private final Method removeMethod;
	private final NativeMapSet nativeMapSet; 
	private final Method hasNextMethod;
	private final Method nextMethod;
	private final Method keyMethod;
	private final Method valueMethod;
	
	/**
	 * Construct native map from std::map wrappers
	 * 
	 * @param nativeKeyClass
	 * @param nativeValueClass
	 * @param mapIteratorClass
	 * @param nativeMap
	 * @param mapWrapperClass
	 */
	public NativeMap(Class<K> nativeKeyClass, Class<V> nativeValueClass, 
			Class<?> mapIteratorClass, Object nativeMap,
			Class<?> mapWrapperClass) {
		this.mapIteratorClass = mapIteratorClass;
		this.nativeMap = nativeMap;
		try {
			mapWrapper = mapWrapperClass.getConstructor(nativeMap.getClass()).
					newInstance(nativeMap);
			sizeMethod = mapWrapperClass.getDeclaredMethod("size");
			addMethod = mapWrapperClass.getDeclaredMethod("add", 
					nativeKeyClass, nativeValueClass);
			clearMethod = mapWrapperClass.getDeclaredMethod("clear");
			removeMethod = mapWrapperClass.getDeclaredMethod("remove", 
					nativeKeyClass);
			hasNextMethod = mapIteratorClass.getDeclaredMethod("hasNext");
			nextMethod = mapIteratorClass.getDeclaredMethod("next");
			keyMethod = mapIteratorClass.getDeclaredMethod("getKey");
			valueMethod = mapIteratorClass.getDeclaredMethod("getValue");
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		nativeMapSet = new NativeMapSet();
	}
	
	@Override
	public Set<Entry<K, V>> entrySet() {
		return nativeMapSet;
	}
	
	@Override
	public V put(K key, V value) {
		V old = get(key);
		entrySet().add(new NativeEntry(key, value));
		return old;
	}

	@Override
	@SuppressWarnings("unchecked")
	public V remove(Object key) {
		V old = get(key);
		if (old != null) {
			entrySet().remove(new NativeEntry((K) key, old));
		}
		return old;
	}

	@Override
	public int size() {
		return entrySet().size();
	}
	
	protected class NativeEntry implements Entry<K, V> {
		private final K key;
		private V value;
		
		protected NativeEntry(K key, V value) {
			this.key = key;
			this.value = value;
		}
		
		@Override
		public K getKey() {
			return key;
		}
		
		@Override
		public V getValue() {
			return value;
		}
		
		@Override
		public V setValue(V value) {
			V old = this.value;
			this.value = value;
			put(key, value);
			return old;
		}
	}
	
	protected class NativeMapSet extends AbstractSet<Entry<K, V>> 
			implements Set<Entry<K, V>> {
		
		@Override
		public boolean add(Entry<K, V> item) {
			try {
				return (Boolean) addMethod.invoke(mapWrapper, 
						item.getKey(), item.getValue());
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}

		@Override
		public void clear() {
			try {
				clearMethod.invoke(mapWrapper);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}

		@Override
		public Iterator<Entry<K, V>> iterator() {
			return new NativeMapSetIterator();
		}

		@Override
		public boolean remove(Object item) {
			try {
				if (item instanceof Entry<?, ?>) {
					return (Boolean) removeMethod.invoke(mapWrapper, 
							((Entry<?, ?>)item).getKey());
				}
				return false;
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}

		@Override
		public boolean removeAll(Collection<?> collection) {
			boolean modified = false;
			for (Object item : collection) {
				modified |= remove(item);
			}
			return modified;
		}
		
		@Override
		public boolean retainAll(Collection<?> collection) {
			//best way?
			List<Entry<K, V>> toRemove = new ArrayList<Entry<K, V>>(this.size());
			for (Entry<K, V> item : this) {
				if (!collection.contains(item)) {
					toRemove.add(item);
				}
			}
			return removeAll(toRemove);
		}

		@Override
		public int size() {
			try {
				return (Integer) sizeMethod.invoke(mapWrapper);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
	}
	
	protected class NativeMapSetIterator implements Iterator<Entry<K, V>> {

		private final Object setIterator;
		
		private NativeMapSetIterator() {
			try {
				setIterator = mapIteratorClass.getConstructor(
						nativeMap.getClass()).newInstance(nativeMap);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		
		@Override
		public boolean hasNext() {
			try {
				return (Boolean) hasNextMethod.invoke(setIterator);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}

		@Override
		@SuppressWarnings("unchecked")
		public NativeEntry next() {
			try {
				nextMethod.invoke(setIterator);
				return new NativeEntry((K) keyMethod.invoke(setIterator), 
						(V) valueMethod.invoke(setIterator));
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}

		/**
		 * {@inheritDoc}
		 * <p>
		 * Unsupported
		 */
		@Override
		public void remove() {
			throw new UnsupportedOperationException();
		}
		
	}
}

NativeSet.java

package org.cretz.swig.collection;

import java.lang.reflect.Method;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * Wrapper for std::set and std::list from SWIG. This does
 * not support removal during iteration.
 * 
 * @author Chad Retz
 * 
 * @param <T>
 */
public class NativeSet<T> extends AbstractSet<T> implements Set<T> {

	private final Class<T> nativeClass;
	private final Class<?> setIteratorClass;
	private final Object nativeSet;
	private final Object setWrapper;
	private final Method sizeMethod;
	private final Method containsMethod;
	private final Method addMethod;
	private final Method clearMethod;
	private final Method removeMethod;
	private final Method hasNextMethod;
	private final Method nextMethod;
	
	/**
	 * Instantiate the native set
	 * 
	 * @param nativeClass The native class
	 * @param setIteratorClass The class for the SetIterator
	 * @param nativeSet The native set object
	 * @param setWrapperClass The class for the Set
	 */
	public NativeSet(Class<T> nativeClass, 
			Class<?> setIteratorClass, Object nativeSet,
			Class<?> setWrapperClass) {
		this.nativeClass = nativeClass;
		this.setIteratorClass = setIteratorClass;
		this.nativeSet = nativeSet;
		try {
			setWrapper = setWrapperClass.getConstructor(nativeSet.getClass()).
					newInstance(nativeSet);
			sizeMethod = setWrapperClass.getDeclaredMethod("size");
			containsMethod = setWrapperClass.getDeclaredMethod("contains", 
					nativeClass);
			addMethod = setWrapperClass.getDeclaredMethod("add", 
					nativeClass);
			clearMethod = setWrapperClass.getDeclaredMethod("clear");
			removeMethod = setWrapperClass.getDeclaredMethod("remove", 
					nativeClass);
			hasNextMethod = setIteratorClass.getDeclaredMethod("hasNext");
			nextMethod = setIteratorClass.getDeclaredMethod("next");
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	@Override
	public boolean add(T item) {
		try {
			return (Boolean) addMethod.invoke(setWrapper, item);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public void clear() {
		try {
			clearMethod.invoke(setWrapper);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public boolean contains(Object item) {
		try {
			return nativeClass.isAssignableFrom(item.getClass()) &&
					(Boolean) containsMethod.invoke(setWrapper, item);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public Iterator<T> iterator() {
		return new NativeSetIterator();
	}

	@Override
	public boolean remove(Object item) {
		try {
			return (Boolean) removeMethod.invoke(setWrapper, item);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public boolean removeAll(Collection<?> collection) {
		boolean modified = false;
		for (Object item : collection) {
			modified |= remove(item);
		}
		return modified;
	}
	
	@Override
	public boolean retainAll(Collection<?> collection) {
		//best way?
		List<T> toRemove = new ArrayList<T>(this.size());
		for (T item : this) {
			if (!collection.contains(item)) {
				toRemove.add(item);
			}
		}
		return removeAll(toRemove);
	}

	@Override
	public int size() {
		try {
			return (Integer) sizeMethod.invoke(setWrapper);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	protected class NativeSetIterator implements Iterator<T> {

		private final Object setIterator;
		
		private NativeSetIterator() {
			try {
				setIterator = setIteratorClass.getConstructor(
						nativeSet.getClass()).newInstance(nativeSet);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		
		@Override
		public boolean hasNext() {
			try {
				return (Boolean) hasNextMethod.invoke(setIterator);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}

		@Override
		@SuppressWarnings("unchecked")
		public T next() {
			try {
				return (T) nextMethod.invoke(setIterator);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}

		/**
		 * {@inheritDoc}
		 * <p>
		 * Unsupported
		 */
		@Override
		public void remove() {
			throw new UnsupportedOperationException();
		}
		
	}
}

Note, only in NativeList is remove supported inside the iterator. All other features should be supported. Let’s review how to use each of these in conjunction with their STL counterparts.

std::list – As mentioned earlier, this must use the NativeSet due to a lack of being indexed. Suppose we have the following C++ reference:

std::list<MyObject*>

Therefore you would need a %template set for both the ListWrapper class and ListIterator class (both in ListWrapper.h above) like so:

%template (MyObjectListWrapper) ListWrapper<MyObject*>;
%template (MyObjectListIterator) ListIterator<MyObject*>;

We’ll assume MyObject was generated by SWIG as the class name MyObject and your std::list was generated by SWIG as the class name SWIGTYPE_p_std__listT_MyObject_p_t. We’ll also assume your original std::list can be obtained via mymodule.getMyObjectList(). Including the above, you should now have two more classes: MyObjectListWrapper and MyObjectListIterator. Now you can either instantiate the NativeSet directly like so:

new NativeSet<MyObject>(MyObject.class, MyObjectListIterator.class, 
        mymodule.getMyObjectList(), MyObjectListWrapper.class);

or you can extend NativeSet and call the super constructor with these values.

std::map – Suppose we have the following C++ reference (This assumes you have “stl.i” as an %import in your SWIG interface):

std::map<std::string, MyObject*>

Therefore you would need a %template set for both the MapWrapper class and MapIterator class (both in MapWrapper.h above) like so:

%template (MyObjectMapWrapper) MapWrapper<MyObject*>;
%template (MyObjectMapIterator) MapIterator<MyObject*>;

We’ll assume MyObject was generated by SWIG as the class name MyObject and your std::map was generated by SWIG as the class name SWIGTYPE_p_std__mapT_std__string_MyObject_p_t. We’ll also assume your original std::map can be obtained via mymodule.getMyObjectMap(). Including the above, you should now have two more classes: MyObjectMapWrapper and MyObjectMapIterator. Now you can either instantiate the NativeMap directly like so:

new NativeMap<String, MyObject>(String.class, MyObject.class,
        MyObjectMapIterator.class, mymodule.getMyObjectMap(),
        MyObjectMapWrapper.class);

or you can extend NativeMap and call the super constructor with these values.

std::set – This is very similar to std::list. Suppose we have the following C++ reference:

std::set<MyObject*>

Therefore you would need a %template set for both the SetWrapper class and SetIterator class (both in SetWrapper.h above) like so:

%template (MyObjectSetWrapper) SetWrapper<MyObject*>;
%template (MyObjectSetIterator) SetIterator<MyObject*>;

We’ll assume MyObject was generated by SWIG as the class name MyObject and your std::set was generated by SWIG as the class name SWIGTYPE_p_std__setT_MyObject_p_t. We’ll also assume your original std::set can be obtained via mymodule.getMyObjectSet(). Including the above, you should now have two more classes: MyObjectSetWrapper and MyObjectSetIterator. Now you can either instantiate the NativeSet directly like so:

new NativeSet<MyObject>(MyObject.class, MyObjectSetIterator.class, 
        mymodule.getMyObjectSet(), MyObjectSetWrapper.class);

or you can extend NativeSet and call the super constructor with these values.

std::vector – Since a vector is indexed, an iterator is not needed here. Suppose we have the following C++ reference:

std::vector<MyObject*>

Therefore you would need a %template set for the VectorWrapper class (in VectorWrapper.h above) like so:

%template (MyObjectVectorWrapper) VectorWrapper<MyObject*>;

We’ll assume MyObject was generated by SWIG as the class name MyObject and your std::vector was generated by SWIG as the class name SWIGTYPE_p_std__vectorT_MyObject_p_t. We’ll also assume your original std::vector can be obtained via mymodule.getMyObjectVector(). Including the above, you should now have another class: MyObjectVectorWrapper. Now you can either instantiate the NativeList directly like so:

new NativeList<MyObject>(MyObject.class, mymodule.getMyObjectVector(),
        MyObjectVectorWrapper.class);

or you can extend NativeList and call the super constructor with these values.

Now you have Java collections representing your STL collections directly. There are several things to note:

  1. Pointers – These collections use the underlying pointers. Therefore, altering a collection here will alter the source collection. Also, since my examples use MyObject as a pointer, they will also be altered by users of this collection. If you don’t want this, instantiate your favorite version of the collection and pass this collection as the parameter; this does a copy
  2. Garbage Collection – Using mutable collections like this can have an issue with SWIG memory ownership. Make sure you read up on SWIG memory management. If you add an object created in Java to this collection, SWIG automatically assumes it owns the collection of this object. Since there will be no references to the object inside of Java, the object may get garbage collected. I usually call Collections.unmodifiable* on this collection because it’s rare I need it changed. Otherwise, you probably instantiated the native collection object in Java too and it should be OK then.
  3. Performance – These collections extend AbstractList, AbstractMap, and AbstractSet. With AbstractList, doing things like remove and contains with the actual object iterate over the entire object. The other two abstracts have similar mechanisms for other pieces. For all collections, equals() iterates through all. Please reference the base classes to understand what they do. If I wasn’t lazy right now, I’d have focused on bridging all STL methods and implement better RandomAccess.
  4. Errors – I only tested a few pieces for right now. I haven’t tested with null objects, or doing things like calling iterator.next() if iterator.hasNext() is false. I purposefully didn’t implement iterator.remove(). Good luck :-)
  5. Thread safety – I have no clue! Be safe and use Collections.synchronized*.
  6. Transformation – Lots of times, you want a cleaner object on the other side of your collections. You can use commons collections‘ CollectionUtils.transformed* (or the more specific ListUtils, MapUtils, and SetUtils). If you’re cool like me, you’d use larvalabs’ collections w/ generics or Google Collections.

Remember, none of this is tested that well and I’m not the strongest C++ dev around so use at your own risk. Once I complete and open source my library that uses this, I will link to the full code and test cases. I hope this code helps someone. License: WTFPL.

About these ads
Tagged with: , , ,

One Response

Subscribe to comments with RSS.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: