Digamos que tengo una función

template<typename T>
some_function(T a){
  // some operations..
}

Tengo una lista enorme de clases cuyos objetos quiero pasar a la función uno por uno (no me pregunten por qué me veo obligado a tenerlo así).

class type1{ //.. whateever is necessary here...
};
class type2{ //.. whateever is necessary here...
};
class type3{ //.. whateever is necessary here...
};
class type4{ //.. whateever is necessary here...
};
.
.
and so on

¿Hay alguna manera de poder crear una instancia de un objeto de cada dato y pasarlo a la función dentro de un ciclo, en lugar de escribirlo uno por uno manualmente? (Sería mejor si la instanciación ocurre dentro del bucle para que el objeto sea local para cada bucle). Cualquier forma de abordar este problema que no sea escribirlo manualmente es bienvenida.

Editar:

Ya que hubo preguntas en los comentarios. Permítanme explicarme el tipo de algoritmo que estoy buscando.

Paso 1: elige una clase my_class in [type1,type2,...,typeN]

Paso 2: crea una instancia de un objeto de esa clase my_class object

Paso 3: páselo a la función some_function(object)

Paso 4: vaya al paso 1 y elija la siguiente clase.

Espero haber dejado las cosas claras.

EDITAR 2: uso c ++ 11. Pero no me importa cambiar si es necesario

1
Panch93 11 nov. 2017 a las 19:55

2 respuestas

La mejor respuesta

Permítanme explicarme el tipo de algoritmo que estoy buscando.

Paso 1: elija una clase my_class en [type1, type2, ..., typeN]

Paso 2: crear una instancia de un objeto de esa clase de objeto my_class

Paso 3: Páselo a la función some_function (objeto)

Paso 4: vaya al paso 1 y elija la siguiente clase.

Si puede usar C ++ 11 o más reciente, y si puede pasar inmediatamente el objeto instanciado a some_function(), puede simular un bucle con una lista de tipos de plantillas variadas de la siguiente manera

template <typename ... Ts>
void repeatOverTypes ()
 {
   using unused=int[];

   (void)unused { 0, (some_function(Ts{}), 0)... };
 }

El siguiente es un ejemplo completo de compilación

#include <iostream>

class type_1 { };
class type_2 { };
class type_3 { };
class type_4 { };


template <typename T>
void some_function (T a)
 { }

template <typename ... Ts>
void repeatOverTypes ()
 {
   using unused=int[];

   (void)unused { 0, (some_function(Ts{}), 0)... };
 }

int main ()
 {
   repeatOverTypes<type_1, type_2, type_3, type_4>();
 }

Si puede usar C ++ 17, usar el plegado repeatOverTypes() se vuelve simple

template <typename ... Ts>
void repeatOverTypes ()
 { (some_function(Ts{}), ...); }

- EDITAR -

La OP dice

Pasé por alto un detalle importante al intentar simplificar el problema. Necesito pasar los objetos por referencia. ¿Entonces el Ts{} no funcionará? Que puedo hacer ?

Ya veo ... bueno, supongo que puede (1) crear el objeto Ts{} y almacenarlo en un contenedor (un std::tuple me parece un contenedor obvio) y (2) pasar a {{ X2}} los valores extraídos de la tupla.

El punto (1) es simple

std::tuple<Ts...> t { Ts{}... };

El punto (2) depende en gran medida de la lista de tipos (hay repeticiones en "type1, type2, ..., typeN"?) Y el idioma exacto.

Si todos los tipos en la lista son diferentes y puede usar C ++ 14, puede acceder a los valores de tupla a través de std::get<Ts>(t); para que la función se pueda escribir

template <typename ... Ts>
void repeatOverTypes ()
 {
   using unused=int[];

   std::tuple<Ts...> t { Ts{}... };

   (void)unused { 0, (some_function(std::get<Ts>(t)), 0)... };
 } 

Si hay repeticiones, debe acceder al valor mediante un índice entero, por lo que debe crear una lista de índices y pasarlos a una función auxiliar; algo como

template <typename T, std::size_t ... Is>
void rotH (T & t, std::index_sequence<Is...> const &)
 {
   using unused=int[];

   (void)unused { 0, (some_function(std::get<Is>(t)), 0)... };

 }

template <typename ... Ts>
void repeatOverTypes ()
 {
   std::tuple<Ts...> t { Ts{}... };

   rotH(t, std::make_index_sequence<sizeof...(Ts)>{});
 } 

Desafortunadamente, std::index_sequence y std::make_index_sequence se introducen en C ++ 14 por lo que, en C ++ 11, debe simularlos de alguna manera.

Como es habitual en C ++ 17, es más sencillo; Si está seguro (pero realmente seguro) de que los tipos son todos diferentes, la función es simplemente

template <typename ... Ts>
void repeatOverTypes ()
 { 
   std::tuple<Ts...> t { Ts{}... };

   (some_function(std::get<Ts>(t)), ...);
 }

En caso de colisión de tipos, con secuencia entera,

template <typename T, std::size_t ... Is>
void rotH (T & t, std::index_sequence<Is...> const &)
 { (some_function(std::get<Is>(t)), ...); }

template <typename ... Ts>
void repeatOverTypes ()
 {
   std::tuple<Ts...> t { Ts{}... };

   rotH(t, std::make_index_sequence<sizeof...(Ts)>{});
 } 
1
max66 11 nov. 2017 a las 22:36

Tengo una lista enorme de clases que objetos quiero pasar a la función una por una

Como parece que necesita manejar muchos tipos y evitar escribirlos codificados en un solo lugar de su código (como se proporciona en esta respuesta), debería considerar el uso de polimorfismo dinámico, interfaces y clases de autorregistro.

Esta es una técnica bien conocida cuando se necesita realizar un conjunto uniforme de operaciones sobre muchos tipos de clases específicos. Muchos marcos de pruebas unitarias usan eso para evitar que se deban agregar casos de prueba adicionales en un lugar central, pero solo dentro de la unidad de traducción donde están definidos.


Aquí hay un boceto (no probado) de cómo implementarlo:

Proporcione una interfaz para describir lo que se debe hacer en some_function() de forma única:

struct IMyInterface {
     virtual ~IMyInterface() {}
     virtual void WhatNeedsToBeDone() = 0;
     virtual int WhatNeedsToBeKnown() const = 0;
};

void some_function(IMyInterface* intf) {
     if(intf->WhatNeedsToBeKnown() == 1) {
          intf->WhatNeedsToBeDone();
     }
}

Proporcione un registrador único que mantenga un mapa de funciones para crear sus clases:

class MyRegistrar {
     MyRegistrar() {};
     using FactoryFunction = std::function<std::unique_ptr<IMyInterface> ()>;
     std::map<std::string, FactoryFunction> classFactories;
public:
     static MyRegistrar& ClassRegistry() {
         static MyRegistrar theRegistrar;
         return theRegistrar;
     };
     template<typename T>
     void registerClassFactory(
         FactoryFunction factory) {
         classFactories[typeid(T).name()] = factory;
     };

     template<typename T>
     std::unique_ptr<IMyInterface> createInstance() {
         return classFactories[typeid(T).name()]();
     }
     template<typename T>
     const FactoryFunction& factory() const {
          return classFactories[typeid(T).name()];
     } 
     std::vector<FactoryFunction> factories() const {
          std::vector<FactoryFunction> result;
          for(auto& factory : classFactories) {
              result.push_back(factory);
          }
          return result;
     } 
};

También proporciona un asistente de registro para facilitar el registro de los tipos con el registrador global

template<typename T>
struct RegistrationHelper {
     RegistrationHelper(
        std::function<std::unique_ptr<IMyInterface> ()> factoryFunc = 
            [](){ return std::make_unique<T>(); }) {
         MyRegistrar::ClassRegistry().registerClassFactory<T>(factoryFunc);
     }         
};

En sus tipos específicos, puede usar eso como

class type1 : public IMyInterface {
    static RegistrationHelper<type1> reghelper;
public:
     void WhatNeedsToBeDone() override {}
     int WhatNeedsToBeKnown() const override { return 0; };
};

RegistrationHelper<type1> type1::reghelper;

También puede especializarse para desviarse de la función predeterminada de fábrica:

enum Color { Red, Green };
class type1 : public IMyInterface {
    static RegistrationHelper<type1> reghelper;
    Color color_;
public:
     type1(Color color) : color_(color) {}
     void WhatNeedsToBeDone() override {}
     int WhatNeedsToBeKnown() const override { return 0; };
};

RegistrationHelper<type1> type1::reghelper(
    [](){ return std::make_unique<type1>(condition? Green : Red);   
    } -> std::function<std::unique_ptr<IMyInterface> ()>
);

Para realizar su iteración sobre todas las clases, puede usar

for(auto factory : MyRegistrar::ClassRegistry().factories()) {
    std::unique_ptr<IMyInterface> intf = factory();
    some_function(intf.get());
}
0
user0042 11 nov. 2017 a las 18:52