F
Frederick Gotham
For objects, we have "void*" as the generic pointer type. For instance:
enum ParamType { Int, Double };
void Func(void *const p,ParamType const pt)
{
switch (pt)
{
case Int: *(int*)p = 42; break;
case Double: *(double*)p = 42; break;
}
}
However, we're not so lucky when it comes to function pointer types. The
behaviour of the following snippet is undefined:
enum ParamType { Int, Double };
void Func(void *const p,ParamType const pt)
{
switch (pt)
{
case Int: ((void(*)(int))p)(42); break;
case Double: ((void(*)(double))p)(42); break;
}
}
I thought I might go about writing a class which would serve as a generic
function pointer. As this was just a pet project, I thought I'd make its
behaviour as well-defined as possible (to the extreme just for the craic!).
I'm designing the class in such a way that it should behave exactly like an
intrinsic function pointer type. I've also taken some liberties in writing
the code, e.g. the behaviour is undefined if you assign from an
uninitialised FuncPtr object. Here's what I've got at the moment:
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <new>
class FuncPtr {
private:
std::size_t quant; /* Size in bytes of function pointer */
void *pbytes;
public:
FuncPtr() : pbytes() {}
template<class T>
FuncPtr(T const p) : quant(sizeof p)
{
pbytes = std::malloc(quant);
if (!pbytes) throw std::bad_alloc();
std::memcpy(pbytes,&p,quant);
}
FuncPtr(FuncPtr const &a) : quant(a.quant)
{
pbytes = std::malloc(quant);
if (!pbytes) throw std::bad_alloc();
std::memcpy(pbytes,a.pbytes,quant);
}
FuncPtr &operator=(FuncPtr const &a)
{
quant = a.quant;
pbytes = std::realloc(pbytes,quant);
if (!pbytes) throw std::bad_alloc();
std::memcpy(pbytes,a.pbytes,quant);
return *this;
}
~FuncPtr() { free(pbytes); }
template<class T>
operator T() const
{
return *(T*)pbytes;
}
};
enum ParamType { Int, Double };
void Foo(FuncPtr const p,ParamType const pt)
{
switch (pt)
{
case Int: ((void(*)(int))p)(5); break;
case Double: ((void(*)(double))p)(5); break;
}
}
#include <iostream>
#include <ostream>
void FuncI(int) { std::cout << "Int function called" << std::endl; }
void FuncD(double) { std::cout << "Double function called" << std::endl; }
int main()
{
FuncPtr a;
FuncPtr const b = FuncD;
a = b;
Foo(a,Double);
Foo(FuncI,Int);
}
I realise it's a bit much to use dynamic memory allocation, but it's sort
of the only way I could _guarantee_ the correct alignment and size (short
of playing around with unions and what have you).
Anywho I just thought I'd throw that out there to see what kind of
responses I get.
enum ParamType { Int, Double };
void Func(void *const p,ParamType const pt)
{
switch (pt)
{
case Int: *(int*)p = 42; break;
case Double: *(double*)p = 42; break;
}
}
However, we're not so lucky when it comes to function pointer types. The
behaviour of the following snippet is undefined:
enum ParamType { Int, Double };
void Func(void *const p,ParamType const pt)
{
switch (pt)
{
case Int: ((void(*)(int))p)(42); break;
case Double: ((void(*)(double))p)(42); break;
}
}
I thought I might go about writing a class which would serve as a generic
function pointer. As this was just a pet project, I thought I'd make its
behaviour as well-defined as possible (to the extreme just for the craic!).
I'm designing the class in such a way that it should behave exactly like an
intrinsic function pointer type. I've also taken some liberties in writing
the code, e.g. the behaviour is undefined if you assign from an
uninitialised FuncPtr object. Here's what I've got at the moment:
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <new>
class FuncPtr {
private:
std::size_t quant; /* Size in bytes of function pointer */
void *pbytes;
public:
FuncPtr() : pbytes() {}
template<class T>
FuncPtr(T const p) : quant(sizeof p)
{
pbytes = std::malloc(quant);
if (!pbytes) throw std::bad_alloc();
std::memcpy(pbytes,&p,quant);
}
FuncPtr(FuncPtr const &a) : quant(a.quant)
{
pbytes = std::malloc(quant);
if (!pbytes) throw std::bad_alloc();
std::memcpy(pbytes,a.pbytes,quant);
}
FuncPtr &operator=(FuncPtr const &a)
{
quant = a.quant;
pbytes = std::realloc(pbytes,quant);
if (!pbytes) throw std::bad_alloc();
std::memcpy(pbytes,a.pbytes,quant);
return *this;
}
~FuncPtr() { free(pbytes); }
template<class T>
operator T() const
{
return *(T*)pbytes;
}
};
enum ParamType { Int, Double };
void Foo(FuncPtr const p,ParamType const pt)
{
switch (pt)
{
case Int: ((void(*)(int))p)(5); break;
case Double: ((void(*)(double))p)(5); break;
}
}
#include <iostream>
#include <ostream>
void FuncI(int) { std::cout << "Int function called" << std::endl; }
void FuncD(double) { std::cout << "Double function called" << std::endl; }
int main()
{
FuncPtr a;
FuncPtr const b = FuncD;
a = b;
Foo(a,Double);
Foo(FuncI,Int);
}
I realise it's a bit much to use dynamic memory allocation, but it's sort
of the only way I could _guarantee_ the correct alignment and size (short
of playing around with unions and what have you).
Anywho I just thought I'd throw that out there to see what kind of
responses I get.