C ++ динамическая реализация с использованием макроса - PullRequest
0 голосов
/ 18 сентября 2018

Я пытаюсь добиться динамической реализации в C ++, что я имею в виду, создавая экземпляры из строк класса. Но после некоторого копания я обнаружил, что, похоже, C ++ изначально не поддерживает такую ​​функцию, как эта.

Например, у меня есть «Интерфейс» с именем Person и некоторые конкретные реализации, такие как Worker, Teacher, Programmer и т. Д. В C ++, если я хочу сохранить все реализации в карте collection, я должен написать что-то вроде этого:

#include <map>
#include <string>

#include "Worker.hpp"
#include "Teacher.hpp"
#include "Programmer.hpp"
// ...

using namespace std;

int main() {
    map<string, Person*> collection;
    Worker worker;
    Teacher teacher;
    Programmer monkey;
    // ...

    collection[worker->title] = &worker;
    collection[teacher->title] = &teacher;
    collection[monkey->tittle] = &monkey;
    // ... 
}

Кажется, все в порядке, но я не люблю менять три места в коде каждый раз, когда добавляю должность. То, что я хотел бы сделать, выглядит следующим образом:

#include <map>
#include <string>
#include "Worker.hpp"
#include "Teacher.hpp"
#include "Programmer.hpp"
# ...

using namespace std;

int main() {
    map<string, Person*> collection;
    char titles[][20] = {"Worker", "Teacher", "Programmer"};

    for (auto const &Title: titles) {
        // this is the magic I would like to have.
        Title object;
        collection[title] = &object;
    }
}

К сожалению, в C ++ нет магии.

Так что через некоторое время я понимаю, что есть макрос и препроцессор, может быть, я смогу обойти это. И вот что я могу придумать до сих пор:

#include <map>
#include <string>
#include <iostream>

#include "Worker.hpp"
#include "Teacher.hpp"
#include "Programmer.hpp"
# ...

#define PUSH(name)\
name o_##name;\
collection[o_##name.title] = &o_##name;\

using namespace std;

int main() {
    map<string, Person*> collection;
    PUSH(Worker);
    PUSH(Teacher);
    PUSH(Programmer);

    return 0;
}

Я пытался автоматизировать директиву #include, но, похоже, в макросе отсутствует понятие переменной. и препроцессор не интерпретирует макрос дважды.

Есть идеи, как мне добиться чего-то подобного?

Я знаю, что звучать глупо - копать свою голову в чем-то бесполезном, но это только моя особенность: я не могу избавиться от этой идеи, если она не оказалась правильной или неправильной.

Я бы очень признателен за совет.

Редактировать:

Спасибо за все ваши усилия, чтобы помочь мне. Я был немного неясен по этому вопросу. Что я на самом деле хотел реализовать, так это

Я хотел создать множество программ и позволить пользователю решать, что запускать. Потенциальная проблема заключается в том, что я мог бы в конечном итоге получить тонны оператора if-else, чтобы позволить пользователю решать, что запускать, потому что я хотел написать тонны маленьких кусочков кода в проекте.

Я не уверен, уясню ли я в этот раз. Но я уже отказался от этой идеи и использовал систему тестирования для выполнения этой работы.

Еще раз всем спасибо.

Ответы [ 2 ]

0 голосов
/ 18 сентября 2018

C ++ обладает магией и называется шаблонами.

Вы можете использовать что-то вроде этого ...

#include <string>
#include <iostream>
#include <map>


template<class T> 
struct make_object {
    static_assert(std::is_convertible<T*, Person*>::value, "Object must be derived from Person");
    T* object;
    make_object() : object(new T) {}
};


//** For iterating over the tuple and inserting each person into the collection
template<std::size_t I = 0, class MAP, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
insertPeople(MAP& collection, std::tuple<Tp...>& t)
{ }

template<std::size_t I = 0, class MAP, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
    insertPeople(MAP& collection, std::tuple<Tp...>& t)
{
    auto object = std::get<I>(t).object;
    collection[object->title] = object;
    insertPeople<I + 1, MAP, Tp...>(collection, t);
}

//** Creates the tuple containing the objects
template<class... CLASSES>
void createPeople(std::map<std::string, Person*>& collection)
{
    std::tuple<make_object<CLASSES>...> objects;
    insertPeople(collection, objects);
}

int main()
{
    std::map<std::string, Person*> collection;
    createPeople<Worker, Teacher, Progammer>(collection);
    for (auto person : collection)
        std::cout << person.second->title << std::endl;
    return 0;
}

Не забудьте удалить все объекты, когда вы закончите.Кроме того, вы можете использовать уникальные / общие указатели.

0 голосов
/ 18 сентября 2018

Вопрос немного неясен относительно отношений типов, времени жизни и т. Д., Но следующий код, вероятно, в основном направлении того, о чем вы просите.

Идея состоит в том, чтобы использовать динамически размещаемые объекты и умный указатель, такой как shared_ptr, для управления временем их жизни.

#include <iostream>
#include <map>              // std::(multimap>
#include <memory>           // std::(make_shared, shared_ptr)
#include <stdlib.h>         // rand, srand
#include <string>
#include <time.h>           // time    
using namespace std;

class Person
{
    string  m_name;

public:
    auto name() const -> string { return m_name; }
    virtual auto profession() const -> string { return ""; }

    Person( const string& name ): m_name( name ) {}
    virtual ~Person() = default;
};

class Worker:
    public Person
{
public:
    auto profession() const -> string override { return "Worker"; }
    Worker( const string& name ): Person( name ) {}
};

class Teacher:
    public Worker
{
public:
    auto profession() const -> string override { return "Teacher"; }
    Teacher( const string& name ): Worker( name ) {}
};

class Programmer:
    public Worker
{
public:
    auto profession() const -> string override { return "Programmer"; }
    Programmer( const string& name ): Worker( name ) {}
};

using Profession_map = multimap<string, shared_ptr<Person>>;

void add_to( Profession_map& persons, const shared_ptr<Person> someone )
{
    persons.emplace( someone->profession(), someone );
};

void list( const Profession_map& persons, const string& profession )
{
    const string profession_description = ( 0?""
        : profession == ""?         "Unknown profession person"
        : profession == "Worker"?   "Generic worker"
        :                           profession
        );

    const int n_registered = persons.count( profession );
    if( n_registered == 1 )
    {
        cout << "There is 1 " << profession_description;
    }
    else
    {
        cout << "There are " << n_registered << " " << profession_description << "s";
    }
    if( n_registered > 0 )
    {
        cout << ": ";
        const auto registered = persons.equal_range( profession );
        int i = 0;
        for( auto it = registered.first; it != registered.second; ++it, ++i )
        {
            if( it != registered.first ) { cout << (i == n_registered - 1? " and " : ", "); }
            const shared_ptr<Person> p_person = it->second;
            cout << p_person->name();
        }
    }
    cout << "." << endl;
}

auto main()
    -> int
{
    Profession_map persons;

    srand( time( nullptr ) );       // Seed for rand() sequence.
    for( const string& name : {"alfa", "beta", "charlie", "delta", "echo", "foxtrot"} )
    {
        switch( rand() % 4 )
        {
            case 0:     add_to( persons, make_shared<Person>( name ) ); break;
            case 1:     add_to( persons, make_shared<Worker>( name ) ); break;
            case 2:     add_to( persons, make_shared<Teacher>( name ) ); break;
            case 3:     add_to( persons, make_shared<Programmer>( name ) ); break;
        }
    }

    list( persons, "" );
    list( persons, "Worker" );
    list( persons, "Teacher" );
    list( persons, "Programmer" );
}
...