使用宏的 C++ 动态实例化
C++ dynamic instantiation using macro
我正在尝试在 C++ 中实现动态实例化,我的意思是,从 class 的字符串创建实例。但经过一番挖掘,我发现 C++ 似乎并不原生支持这样的功能。
例如,我有一个名为Person
的"Interface",还有一些具体实现如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
语句让用户决定要 运行 的内容,因为我想在项目中编写大量的小代码。
不知道这次我说清楚没有。但是我已经放弃了这个想法,并使用测试框架来完成这项工作。
再次感谢大家。
这个问题在类型关系、生命周期等方面有点不清楚,但下面的代码可能是你所要求的主要方向。
想法是使用动态分配的对象,以及 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" );
}
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;
}
完成后不要忘记删除所有对象。或者,您可以使用 unique/shared 指针。
我正在尝试在 C++ 中实现动态实例化,我的意思是,从 class 的字符串创建实例。但经过一番挖掘,我发现 C++ 似乎并不原生支持这样的功能。
例如,我有一个名为Person
的"Interface",还有一些具体实现如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
语句让用户决定要 运行 的内容,因为我想在项目中编写大量的小代码。
不知道这次我说清楚没有。但是我已经放弃了这个想法,并使用测试框架来完成这项工作。
再次感谢大家。
这个问题在类型关系、生命周期等方面有点不清楚,但下面的代码可能是你所要求的主要方向。
想法是使用动态分配的对象,以及 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" );
}
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;
}
完成后不要忘记删除所有对象。或者,您可以使用 unique/shared 指针。