¿No sería más fácil implementar una versión no operativa sobrecargada de print (void)?
Ahh bien. Las plantillas de funciones y la sobrecarga manejarán esto fácilmente en el tiempo de ejecución.
Se pone algo más pegajoso si hubiera querido manejar esto en tiempo de compilación, para usar con macros #if o compilaciones de tiempo de compilación estática.
Pero ya que sólo desea que el primero, puedo sugerir algo así como un punto de partida:.
(Probado bajo (GCC) 3.4.4 4.0.1 & - Lo sé, lo que tenga que actualizar !)
#include <iostream>
using namespace std;
struct Foo {
void operator()() {}
};
struct Bar {
bool operator()() { return false; }
};
Foo foo;
Bar bar;
bool baz() { return false; }
void bang() {}
struct IsVoid
{
typedef char YES[1];
typedef char NO[2];
/* Testing functions for void return value. */
template <typename T>
static IsVoid::NO & testFunction(T (*f)());
static IsVoid::YES & testFunction(void (*f)());
static IsVoid::NO & testFunction(...);
/* Testing Objects for "void operator()()" void return value. */
template <typename C, void (C::*)()>
struct hasOperatorMethodStruct { };
template <typename C>
static YES & testMethod(hasOperatorMethodStruct<C, &C::operator()> *);
template <typename C>
static NO & testMethod(...);
/* Function object method to call to perform test. */
template <typename T>
bool operator() (T & t)
{
return ( (sizeof(IsVoid::testFunction(t)) == sizeof(IsVoid::YES))
|| (sizeof(IsVoid::testMethod<T>(0)) == sizeof(IsVoid::YES)));
}
};
#define BOUT(X) cout << # X " = " << boolToString(X) << endl;
const char * boolToString(int theBool)
{
switch (theBool)
{
case true: return "true";
case false: return "false";
default: return "unknownvalue";
}
}
int main()
{
IsVoid i;
BOUT(IsVoid()(foo));
BOUT(IsVoid()(bar));
BOUT(IsVoid()(baz));
BOUT(IsVoid()(bang));
cout << endl;
BOUT(i(foo));
BOUT(i(bar));
BOUT(i(baz));
BOUT(i(bang));
}
bien, comienzo a ver más del problema.
Mientras que podemos hacer algo en la línea de lo siguiente:
#include <iostream>
using namespace std;
struct FooA {
void operator()() {}
};
struct FooB {
bool operator()() { return false; }
};
struct FooC {
int operator()() { return 17; }
};
struct FooD {
double operator()() { return 3.14159; }
};
FooA fooA;
FooB fooB;
FooC fooC;
FooD fooD;
void barA() {}
bool barB() { return false; }
int barC() { return 17; }
double barD() { return 3.14159; }
namespace N
{
/* Functions */
template <typename R>
R run(R (*f)()) { return (*f)(); }
bool run(void (*f)()) { (*f)(); return true; }
/* Methods */
template <typename T, typename R>
R run(T & t, R (T::*f)()) { return (t .* f)(); }
template <typename T>
bool run(T & t, void (T::*f)()) { (t .* f)(); return true; }
};
#define SHOW(X) cout << # X " = " << (X) << endl;
#define BOUT(X) cout << # X " = " << boolToString(X) << endl;
const char * boolToString(int theBool)
{
switch (theBool)
{
case true: return "true";
case false: return "false";
default: return "unknownvalue";
}
}
int main()
{
SHOW(N::run(barA));
BOUT(N::run(barA));
SHOW(N::run(barB));
BOUT(N::run(barB));
SHOW(N::run(barC));
SHOW(N::run(barD));
cout << endl;
SHOW(N::run(fooA,&FooA::operator()));
BOUT(N::run(fooA,&FooA::operator()));
SHOW(N::run(fooB,&FooB::operator()));
BOUT(N::run(fooB,&FooB::operator()));
SHOW(N::run(fooC,&FooC::operator()));
SHOW(N::run(fooD,&FooD::operator()));
}
Usted todavía tiene que hacer desagradable necesidad de alimentar & CLASE :: operador() como argumento.
En última instancia, si bien podemos determinar si el operador del objeto () método devuelve un vacío, no podemos normalmente sobrecarga basado en tipos de retorno.
Podemos superar esa limitación de sobrecarga mediante la especialización de plantilla. Pero luego nos metemos en esta fealdad en la que todavía necesitamos especificar tipos ... ¡Especialmente el tipo de devolución! Ya sea manualmente o pasando un argumento adecuado desde el cual podemos extraer los tipos necesarios.
BTW: #define macros no ayudará tampoco. Herramientas como?: Requieren el mismo tipo para ambos? y: parte
Así que esto es lo mejor que puedo hacer ...
Por supuesto ...
Si usted no necesita el tipo de retorno ...
Si solo está pasando el resultado a otra función ...
Puede hacer algo como esto:
#include <iostream>
using namespace std;
struct FooA {
void operator()() {}
};
struct FooB {
bool operator()() { return false; }
};
struct FooC {
int operator()() { return 17; }
};
struct FooD {
double operator()() { return 3.14159; }
};
FooA fooA;
FooB fooB;
FooC fooC;
FooD fooD;
void barA() {}
bool barB() { return false; }
int barC() { return 17; }
double barD() { return 3.14159; }
#define SHOW(X) cout << # X " = " << (X) << endl;
namespace N
{
template <typename T, typename R>
R run(T & t, R (T::*f)()) { return (t .* f)(); }
template <typename T>
bool run(T & t, void (T::*f)()) { (t .* f)(); return true; }
template <typename T>
void R(T & t)
{
SHOW(N::run(t, &T::operator()));
}
template <typename T>
void R(T (*f)())
{
SHOW((*f)());
}
void R(void (*f)())
{
(*f)();
SHOW(true);
}
};
int main()
{
N::R(barA);
N::R(barB);
N::R(barC);
N::R(barD);
N::R(fooA);
N::R(fooB);
N::R(fooC);
N::R(fooD);
}
Probado y funciona en gcc 4.2.1. –
La "impresión" es solo para probar lo que está pasando ... lo que estoy haciendo es envolver métodos y funciones para pasar al mecanismo de devolución de llamada asincrónico de otra persona. La mayoría de mis métodos y funciones vuelven vacíos, a veces necesito poder devolver un valor booleano a la devolución de llamada asíncrona de un tercero. –
¡Esto es muy resbaladizo! No hay uno o dos sino tres trucos que no entendí bien, uno de los cuales son los dos casos NO en la sobrecarga de testFunction, uno es el violín hasOperatorMethodStruct, y el otro es la manera de combinar estas dos condiciones. Muy educativo, te debo una bebida refrescante de tu elección. –