tengo algunas clases, que por diversas razones fuera del alcance de esta discusión, no puede modificar (los detalles de implementación irrelevantes omitidas):Adaptación de envases no iterables que se itera a través de encargo a plantillas iterador
class Foo { /* ... irrelevant public interface ... */ };
class Bar {
public:
Foo& get_foo(size_t index) { /* whatever */ }
size_t size_foo() { /* whatever */ }
};
(Hay hay muchas clases similares 'Foo' y 'Bar' con las que estoy tratando, y todo es código generado de otra parte y cosas que no quiero subclasificar, etc.)
[Editar: aclaración - aunque hay muchos clases similares 'Foo' y 'Bar', se garantiza que cada clase "externa" tendrá los métodos getter y size. Solo el nombre del método getter y el tipo de retorno diferirán para cada "exterior", según el tipo que contenga "interno".
Por lo tanto, si tengo que contiene Baz casos quux, habrá quux & Baz :: get_quux (size_t índice), y size_t Baz :: size_quux().]
dado el diseño de la clase Bar , no puede usarlo fácilmente en algoritmos AWL (por ejemplo, for_each, find_if, etc.) y debe realizar bucles imperativos en lugar de adoptar un enfoque funcional (razones por las que prefiero que el último también esté fuera del alcance de esta discusión):
Bar b;
size_t numFoo = b.size_foo();
for (int fooIdx = 0; fooIdx < numFoo; ++fooIdx) {
Foo& f = b.get_foo(fooIdx);
/* ... do stuff with 'f' ... */
}
Así que ... Nunca he creado un iterador personalizado, y después de leer varias preguntas/respuestas en SO sobre iterator_traits y similares, se me ocurrió esta "solución" (actualmente medio cocida):
Primero, el mecanismo iterador personalizado (NOTA: todos los usos de 'función' y 'vincular' son de std :: tr1 en MSVC9):
// Iterator mechanism...
template <typename TOuter, typename TInner>
class ContainerIterator : public std::iterator<std::input_iterator_tag, TInner> {
public:
typedef function<TInner& (size_t)> func_type;
ContainerIterator(const ContainerIterator& other) : mFunc(other.mFunc), mIndex(other.mIndex) {}
ContainerIterator& operator++() { ++mIndex; return *this; }
bool operator==(const ContainerIterator& other) {
return ((mFunc.target<TOuter>() == other.mFunc.target<TOuter>()) && (mIndex == other.mIndex));
}
bool operator!=(const ContainerIterator& other) { return !(*this == other); }
TInner& operator*() { return mFunc(mIndex); }
private:
template<typename TOuter, typename TInner>
friend class ContainerProxy;
ContainerIterator(func_type func, size_t index = 0) : mFunc(func), mIndex(index) {}
function<TInner& (size_t)> mFunc;
size_t mIndex;
};
a continuación, el mecanismo por el cual consigo iteradores válidas que representan comenzar y al final del contenedor interior:
// Proxy(?) to the outer class instance, providing a way to get begin() and end()
// iterators to the inner contained instances...
template <typename TOuter, typename TInner>
class ContainerProxy {
public:
typedef function<TInner& (size_t)> access_func_type;
typedef function<size_t()> size_func_type;
typedef ContainerIterator<TOuter, TInner> iter_type;
ContainerProxy(access_func_type accessFunc, size_func_type sizeFunc) : mAccessFunc(accessFunc), mSizeFunc(sizeFunc) {}
iter_type begin() const {
size_t numItems = mSizeFunc();
if (0 == numItems) return end();
else return ContainerIterator<TOuter, TInner>(mAccessFunc, 0);
}
iter_type end() const {
size_t numItems = mSizeFunc();
return ContainerIterator<TOuter, TInner>(mAccessFunc, numItems);
}
private:
access_func_type mAccessFunc;
size_func_type mSizeFunc;
};
puedo utilizar estas clases de la siguiente manera:
// Sample function object for taking action on an LMX inner class instance yielded
// by iteration...
template <typename TInner>
class SomeTInnerFunctor {
public:
void operator()(const TInner& inner) {
/* ... whatever ... */
}
};
// Example of iterating over an outer class instance's inner container...
Bar b; /* assume populated which contained items ... */
ContainerProxy<Bar, Foo> bProxy(
bind(&Bar::get_foo, b, _1),
bind(&Bar::size_foo, b));
for_each(bProxy.begin(), bProxy.end(), SomeTInnerFunctor<Foo>());
Empíricamente, esta solución funciona correctamente (menos cualquier copia/pega o errores tipográficos que pueda haber introducido al editar lo anterior para abreviar).
Así que, finalmente, la pregunta real:
no me gusta que requiere el uso de bind() y _1 marcadores de posición, etcétera por la persona que llama. Todo lo que realmente les importa es: tipo externo, tipo interno, método del tipo externo para captar instancias internas, método del tipo externo para captar instancias internas.
¿Hay alguna manera de "ocultar" el enlace en el cuerpo de las clases de plantilla de alguna manera? No he podido encontrar una manera de suministrar por separado los parámetros de plantilla para los tipos y métodos internos por separado ...
¡Gracias!
David
Si todo lo que busca es evitar 'std :: bind' y' std :: placeholders', puede abusar del hecho de que, p. 'std :: function' es perfectamente feliz de tragar el tipo de función puntero-a-miembro 'Foo & (Bar :: *) (size_t)' –
Managu