I'm working on an NES emulator in c++ and figured that the most efficient way to run opcodes would be to call a function pointer in an array of functions that do exactly what the opcode does.
The problem is that each opcode has a specific operation and memory address. While searching for a solution, I stumbled upon lambda expressions. This is definitely good enough for a NES emulator on modern hardware. However, I can't find a solution such that each function in the array contains the machine code for both the operation and the addressing without defining 256 separate functions.
This is along what I had in mind for a similar function that combines f and g:
int addone(int x) {
return x + 1;
}
int multtwo(int x) {
return 2 * x;
}
something combine(function<int(int)> f, function <int(int)> g) {
/* code here */
}
/*
combine(addone, multtwo) creates a function h that has the same machine code as
int h(x) {
return 2 * x + 1;
}
*/
Any ideas? If I had to take a guess, it would have something to do with templates. Thanks!
I'd say that when you want to write generics for functions that it's kind of a "design pattern" to switch to functors: Compilers are desigined to handle types easily, but handling function pointers for stuff you want to mis-match and keep optimised at compile-time gets ugly!
So we either write our functions as functors, or we wrap them as functors:
struct A
{
static constexpr int Func (int x)
{
return -3*x + 1;
}
};
struct B
{
static constexpr int Func (int x)
{
return -2*x - 5;
}
};
// etc...
If we have nice symmetry in how we'll use them, then we can manage them systematically. Eg. if we always want to combine them like f(g(h(...y(z())...)))
, then we can solve as follows:
template <class T, class ... Ts>
struct Combine
{
static constexpr int Get ()
{
int x = Combine<Ts...>::Get();
return T::Func(x);
}
};
template <class T>
struct Combine <T> // The base case: the last function in the list
{
static constexpr int Get ()
{
return T::Func();
}
};
Or if we're in no such luck, we'll have to resort to more old-fashioned inputs like you suggested:
template <class Funcs, class Data>
constexpr int Combine (const Data & d)
{
Funcs F;
// Some use without much symmetry:
return F.f(F.g(d)) - F.h(d);
}
int main ()
{
struct FuncArgs
{
A f;
B g;
C h;
};
return Combine<FuncArgs>(5);
}
Note that in the second example I've changed from static methods to non-static. This doesn't really matter - the compiler should optimise these fully regardless, but I think in this case it makes the syntax slightly nicer (and shows an alternative style).