我试图想出一个更好的方式来代表以下内容:
using InsBase0 = std::tuple<std::string, std::function<void()>>;
static const std::array<InsBase0, 1> ins0_bases = {{
{"NOP", 0x0},
}};
using InsBase1 = std::tuple<std::string, std::function<void(const std::string &)>>;
static const std::array<InsBase1, 7> ins1_bases = {{
{"INC", 0x0},
{"DEC", 0x0},
{"AND", 0x0},
{"OR", 0x0},
{"XOR", 0x0},
{"CP", 0x0},
{"SUB", 0x0},
}};
using InsBase2 = std::tuple<std::string, std::function<void(const std::string &, const std::string&)>>;
static const std::array<InsBase2, 6> ins_bases = {{
{"LD", 0x0},
{"ADD", 0x0},
{"ADC", 0x0},
{"SBC", 0x0},
{"JP", 0x0},
{"JR", 0x0},
}};
(完全做作的例子,想象代替0x0
的函数和更像地图而不是数组或结构而不是元组的东西)
上下文是这是一个汇编程序,所以我需要将指令映射到函数。
在一个完美的世界中,我希望能够将所有指令放入一个数组/容器中(使用额外的args
成员来表示该函数所需的args数量),但是我很高兴不会使用StructName0
重复定义
答案 0 :(得分:2)
两位元编程助手:
template<std::size_t I>
using index=std::integral_constant<std::size_t, I>;
template<class T> struct tag_t {constexpr tag_t(){};};
template<class T> tag_t<T> tag{};
template<std::size_t, class T>
using indexed_type = T;
现在我们为每个参数计数定义一个枚举类型:
enum class zero_op:std::size_t { NOP };
enum class one_op:std::size_t { INC };
enum class two_op:std::size_t { ADD };
接下来,从类型到参数计数的映射:
constexpr index<0> args( tag_t<zero_op> ) { return {}; }
constexpr index<1> args( tag_t<one_op> ) { return {}; }
constexpr index<2> args( tag_t<two_op> ) { return {}; }
这需要模板,计数和类型,并重复将类型传递给模板:
template<template<class...>class Z, class T, class Indexes>
struct repeat;
template<template<class...>class Z, class T, std::size_t I>
struct repeat<Z, T, index<I>>:
repeat<Z, T, std::make_index_sequence<I>>
{};
template<template<class...>class Z, class T, std::size_t...Is>
struct repeat<Z, T, std::index_sequence<Is...>> {
using type=Z<indexed_type<Is, T>...>;
};
template<template<class...>class Z, class T, std::size_t N>
using repeat_t = typename repeat<Z, T, index<N>>::type;
我们使用它来构建我们的std::function
签名:
template<class...Args>
using void_call = std::function<void(Args...)>;
template<std::size_t N, class T>
using nary_operation = repeat_t< void_call, T, N >;
且nary_operation< 3, std::string const& >
为std::function<void(std::string const&,std::string const&,std::string const&)>
。
我们使用它来创建编译时多态表:
template<class...Es>
struct table {
template<class E>
using operation = nary_operation<args(tag<E>), std::string const&>;
template<class E>
using subtable = std::map< E, operation<E> >;
std::tuple< subtable<Es>... > tables;
template<class E>
operation<E> const& operator[]( E e ) {
return std::get< subtable<E> >( tables )[e];
}
};
或类似的东西。
如果你有table<zero_op, one_op, two_op> bob
的内容,你可以
bob[ zero_op::NOP ]();
或
bob[ zero_op::INC ]("foo");
或
bob[ zero_op::ADD ]("foo", "bar");
[]
中枚举的类型会更改返回的函数对象的类型。
以上可能有拼写错误。
但是,最终结果是类型安全的。
答案 1 :(得分:1)
您可以使用std::string
代替函数的std::vector<std::string>
参数,这样就可以存储多个参数。
这与以下内容有关:
using function_t = std::function<void(const std::vector<std::string>&)>;
static const std::unordered_map<std::string, function_t> instructions =
{
{"INC", somefunc},
...
};
然后拨打正确的指示:
std::vector<std::string> arguments = { "zob", "zeb" };
auto result = instructions["INC"](arguments);
编辑:
以下是我将如何做的其余部分,以证明你不会那么久:
/**
* Your instruction type. Contains its name,
* the function to call, and the number of args required
*/
struct Instruction {
using function_t = std::function<void(std::vector<std::string>)>;
std::string name;
function_t function;
std::size_t numargs;
Instruction(const std::string& name = "undefined", const function_t& function = function_t(), std::size_t numargs = 0)
: name(name)
, function(function)
, numargs(numargs) {}
}
/**
* Your instruction set. It contains the instructions you want to register in.
* You can call the instructions safely through this
*/
struct InstructionSet {
std::unordered_map<std::string, Instruction> instructions;
void callInstruction(const std::string& inst_name, const std::vector<std::string>& arguments) {
if (!instructions.count(inst_name))
return; // no instruction named "inst_name", return or throw something relevant
auto instruction = instructions[inst_name];
if (instruction.numargs != arguments.size())
return; // too many / not enough parameters, return or throw something relevant
instruction.function(arguments);
}
void registerInstruction(const Instruction& instruction) {
instructions[instruction.name] = instruction;
}
};
int main() {
InstructionSet instruction_set;
instruction_set.registerInstruction(Instruction(
"INC",
[](const std::vector<std::string>& arguments) {
bake_cookies_plz(arguments);
},
2)
);
instruction_set.callInstruction("INC", { "1", "2" });
return 0;
}
答案 2 :(得分:1)
我无法找到一种方法将所有操作存储在一个结构中,并且仍然有编译时间检查。但是可以在运行时检查传递的值的数量。
#include <iostream>
#include <functional>
#include <string>
#include <unordered_map>
class operation
{
using op0_funcptr = void(*)();
using op1_funcptr = void(*)(const std::string&);
using op2_funcptr = void(*)(const std::string&, const std::string&);
using op0_func = std::function<void()>;
using op1_func = std::function<void(const std::string&)>;
using op2_func = std::function<void(const std::string&, const std::string&)>;
std::tuple<
op0_func,
op1_func,
op2_func> m_functions;
public:
operation() :m_functions(op0_func(), op1_func(), op2_func()) {}
operation(const op0_func& op) :m_functions(op, op1_func(), op2_func()) {}
operation(const op0_funcptr& op) :m_functions(op, op1_func(), op2_func()) {}
operation(const op1_func& op) :m_functions(op0_func(), op, op2_func()) {}
operation(const op1_funcptr& op) :m_functions(op0_func(), op, op2_func()) {}
operation(const op2_func& op) :m_functions(op0_func(), op1_func(), op) {}
operation(const op2_funcptr& op) :m_functions(op0_func(), op1_func(), op) {}
operation(const operation& other) = default;
operation(operation&& other) = default;
void operator()() { std::get<op0_func>(m_functions)(); }
void operator()(const std::string& p1) { std::get<op1_func>(m_functions)(p1); }
void operator()(const std::string& p1, const std::string& p2) { std::get<op2_func>(m_functions)(p1, p2); }
};
void nop()
{
std::cout << "NOP" << std::endl;
}
void inc(const std::string& p1)
{
std::cout << "INC(" << p1 << ")" << std::endl;
}
void add(const std::string& p1, const std::string& p2)
{
std::cout << "ADD(" << p1 << ", " << p2 << ")" << std::endl;
}
std::unordered_map<std::string, operation> operations{ {
{ "NOP", nop },
{ "INC", inc },
{ "ADD", add }
} };
int main(int argc, char** argv)
{
operations["NOP"]();
operations["INC"]("R1");
operations["ADD"]("R2", "R3");
operations["ADD"]("R2"); //Throws std::bad_function_call
}
到目前为止,这不是最佳解决方案,但它确实有效。
如果您想要更快地访问,您还可以尝试将下半部分更改为:
enum class OP : size_t
{
NOP,
INC,
ADD,
NUM_OPS
};
std::array<operation, (size_t)OP::NUM_OPS> operations{ nop ,inc, add };
int main(int argc, char** argv)
{
operations[(size_t)OP::NOP]();
operations[(size_t)OP::INC]("R1");
operations[(size_t)OP::ADD]("R2", "R3");
//operations[(size_t)OP::ADD]("R2"); //Throws std::bad_function_call
}
答案 3 :(得分:1)
我建议使用单std::map
,其中键是函数的名称(NOP
,AND
,ADD
等。)
使用继承,一个简单的基类,一个std::function
包装器......
我想,不是很优雅,但是......
#include <map>
#include <memory>
#include <iostream>
#include <functional>
struct funBase
{
// defined only to permit the dynamic_cast
virtual void unused () const {};
};
template <typename R, typename ... Args>
struct funWrap : public funBase
{
std::function<R(Args...)> f;
funWrap (R(*f0)(Args...)) : f { f0 }
{ }
};
template <typename R, typename ... Args>
std::unique_ptr<funBase> makeUFB (R(*f)(Args...))
{ return std::unique_ptr<funBase>(new funWrap<R, Args...>(f)); }
template <typename F, typename T, bool = std::is_convertible<F, T>::value>
struct getConv;
template <typename F, typename T>
struct getConv<F, T, true>
{ using type = T; };
template <typename F, typename T>
struct getConv<F, T, false>
{ };
template <typename ... Args>
void callF (std::unique_ptr<funBase> const & fb, Args ... args)
{
using derType = funWrap<void,
typename getConv<Args, std::string>::type const & ...>;
derType * pdt { dynamic_cast<derType *>(fb.get()) };
if ( nullptr == pdt )
std::cout << "call(): error in conversion" << std::endl;
else
pdt->f(args...);
}
void fNop ()
{ std::cout << "NOP!" << std::endl; }
void fAnd (std::string const & s)
{ std::cout << "AND! [" << s << ']' << std::endl; }
void fAdd (std::string const & s1, std::string const & s2)
{ std::cout << "ADD! [" << s1 << "] [" << s2 << ']' << std::endl; }
int main()
{
std::map<std::string, std::unique_ptr<funBase>> fm;
fm.emplace("NOP", makeUFB(fNop));
fm.emplace("AND", makeUFB(fAnd));
fm.emplace("ADD", makeUFB(fAdd));
callF(fm["NOP"]); // print NOP!
callF(fm["AND"], "arg"); // print AND! [arg]
callF(fm["ADD"], "arg1", "arg2"); // print ADD! [arg1] [arg2]
callF(fm["ADD"], "arg1"); // print call(): error in conversion
//callF(fm["ADD"], "arg1", 12); // compilation error
return 0;
}
P.s。:也适用于C ++ 11。