Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » statisch die existenz von methoden feststellen

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
09.07.2010, 13:12 Uhr
~ex-bart
Gast


Hi!

Ich habe eine Template-Klasse, die an einem ihrer Template-Parameter Methoden
aufruft:

C++:
template<typename T>
struct caller {
  static void call(const T& t, int p1, int p2) {
    t.foo(p1, p2);
  }
};


Problem ist, daß das auch für Template Parameter T funktionieren soll, wenn es
nur die methode foo(int) gibt, dann soll der parameter p2 ignoriert werden,
etwa so:

C++:
template<typename T, bool cond = has_two_argument_foo<T>::value>
struct caller {
  static void call(const T& t, int p1, int p2) {
    t.foo(p1, p2);
  }
};
template<typename T>
struct caller<T, false> {
  static void call(const T& t, int p1, int p2) {
    t.foo(p1);
  }
};


Die Frage ist nun, wie das Template
C++:
has_two_argument_foo
aussehen
muß. Daß das ganze prinzipiell möglich ist, weiß ich, da es mit Boost.Typeof
im Emulationsmodus geht:

C++:
#define BOOST_TYPEOF_COMPLIANT
#include <type_traits>
#include <boost/typeof/typeof.hpp>

template<typename T>
struct always_false
  : std::false_type
{ };

template<typename LOP, typename = std::false_type>
struct has_two_argument_foo :
  public std::false_type
{ };
template<typename LOP>
struct has_two_argument_foo<LOP, typename always_false<BOOST_TYPEOF_TPL(((LOP*)0)->foo(0,0))>::type > :
  public std::true_type
{ };


Diese Lösung hat allerdings zwei Probleme: erstens kann ich auf meiner
Zielplattform nicht mit Boost rechnen, und zweitens funktioniert sie nicht,
wenn der Rückgabetyp von foo() void ist. Ich habe erfolglos versucht, zu
verstehen, was die typeof-Emulation tut, und das Dokument, das die
Implementierung der typeof-Emulation beschreibt scheint nicht mehr zu
existieren.

Kann mir jemand weiterhelfen?

Hier ist nochmal der Komplette code meines Testprograms. Übersetzen muß man
das ganze mit "g++ --std=c++0x" oder so ähnlich, damit der compiler den header
<type_traits> kennt.

C++:
#include <iostream>
#include <type_traits>
#include <boost/typeof/typeof.hpp>

template<typename T>
struct always_false
  : std::false_type
{ };

template<typename LOP, typename = std::false_type>
struct has_two_argument_foo :
  public std::false_type
{ };
template<typename LOP>
struct has_two_argument_foo<LOP, typename always_false<BOOST_TYPEOF_TPL(((LOP*)0)->foo(0,0))>::type > :
  public std::true_type
{ };

template<typename T, bool cond = has_two_argument_foo<T>::value>
struct caller {
  static void call(T& t, int p1, int p2) {
    t.foo(p1, p2);
  }
};
template<typename T>
struct caller<T, false> {
  static void call(T& t, int p1, int p2) {
    t.foo(p1);
  }
};

struct LOP1 {
  int foo(int a, int b) {
    std::cout << "LOP1 (two arguments)" << std::endl;
    return 0;
  }
  int foo(int a) {
    std::cout << "LOP1 (one argument)" << std::endl;
    return 0;
  }
};

struct LOP2 {
  int foo(int a) {
    std::cout << "LOP2 (one argument)" << std::endl;
    return 0;
  }
};

int main() {
  LOP1 lop1;
  LOP2 lop2;
  caller<LOP1>::call(lop1, 0, 1);
  caller<LOP2>::call(lop2, 0, 1);
}

 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
09.07.2010, 14:39 Uhr
0xdeadbeef
Gott
(Operator)


Oooh, Mindbender. Also, das ganze ohne Boost nachzubauen ist relativ einfach:

C++:
#include <cstddef>
#include <iostream>

struct false_type { };
struct true_type  { };

template<std::size_t> struct sfinae_helper { typedef true_type type; };

template<typename T, typename = true_type>
struct has_two_argument_foo {
  static bool const value = false;
};

template<typename T>
struct has_two_argument_foo<T, typename sfinae_helper<sizeof(static_cast<T*>(0)->foo(0, 0))>::type> {
  static bool const value = true;
};

struct A { };
struct B { int foo(int, int) { } };
struct C { void foo(int, int) { } };
struct D { void foo(int) { } };

int main() {
  std::cout << has_two_argument_foo<A>::value << std::endl
            << has_two_argument_foo<B>::value << std::endl
            << has_two_argument_foo<C>::value << std::endl
            << has_two_argument_foo<D>::value << std::endl;
}


C liegt da aber immer noch daneben (sizeof(void) kann nicht aufgelöst werden). Ich muss darüber mal ein bisschen meditieren und melde mich später wieder.
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
09.07.2010, 15:06 Uhr
0xdeadbeef
Gott
(Operator)


So geht das schon besser:

C++:
#include <cstddef>
#include <iostream>

struct false_type { };
struct true_type  { };

template<std::size_t> struct sfinae_helper { typedef true_type type; };

template<typename T, typename R, typename A1, typename A2>
true_type sfinae_memfunc_helper(R (T::*)(A1, A2), A1, A2) {
  return true_type();
}

template<typename T, typename = true_type>
struct has_two_argument_foo {
  static bool const value = false;
};

template<typename T>
struct has_two_argument_foo<T, typename sfinae_helper<sizeof(sfinae_memfunc_helper(&T::foo, 0, 0))>::type> {
  static bool const value = true;
};

struct A { };
struct B { int  foo(int, int) { } };
struct C { void foo(int, int) { } };
struct D { void foo(int) { } };

struct E { int  foo(false_type, true_type) { } };
struct F { void foo(false_type, true_type) { } };

int main() {
  std::cout << has_two_argument_foo<A>::value << std::endl
            << has_two_argument_foo<B>::value << std::endl
            << has_two_argument_foo<C>::value << std::endl
            << has_two_argument_foo<D>::value << std::endl
            << has_two_argument_foo<E>::value << std::endl
            << has_two_argument_foo<F>::value << std::endl;
}


Dabei wird jetzt auch geprüft, ob T::foo Integer als Argumente akzeptiert (deswegen liefert die Metafunktion für E und F false). Der Name "has_two_argument_foo" ist da etwas unpraktisch gewählt, aber für eine Demonstration sollte das genügen.

Hilft dir das weiter?
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra

Dieser Post wurde am 09.07.2010 um 15:08 Uhr von 0xdeadbeef editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
09.07.2010, 15:16 Uhr
0xdeadbeef
Gott
(Operator)


Ups, da ist mir noch ein Malheur unterlaufen. Eine Indirektion brauchen wir noch, um andere int-Typen, doubles, und sonstiges, was Integer annimmt, zu erschlagen:

C++:
#include <cstddef>
#include <iostream>

struct false_type { };
struct true_type  { };

template<std::size_t> struct sfinae_helper { typedef true_type type; };

template<typename A1, typename A2>
struct sfinae_call_proxy {
  int operator()(A1, A2);
};

template<typename T, typename R, typename A1, typename A2>
sfinae_call_proxy<A1, A2> sfinae_memfunc_helper(R (T::*)(A1, A2));

template<typename T, typename = true_type>
struct has_two_argument_foo {
  static bool const value = false;
};

template<typename T>
struct has_two_argument_foo<T, typename sfinae_helper<sizeof(sfinae_memfunc_helper(&T::foo)(0, 0))>::type> {
  static bool const value = true;
};

struct A { };
struct B { int  foo(int, int) { } };
struct C { void foo(int, int) { } };
struct D { void foo(int) { } };

struct E { int  foo(false_type, true_type) { } };
struct F { void foo(false_type, true_type) { } };

struct G { int  foo(double, double) { } };
struct H { void foo(int, double) { } };

int main() {
  std::cout << has_two_argument_foo<A>::value << std::endl
            << has_two_argument_foo<B>::value << std::endl
            << has_two_argument_foo<C>::value << std::endl
            << has_two_argument_foo<D>::value << std::endl
            << has_two_argument_foo<E>::value << std::endl
            << has_two_argument_foo<F>::value << std::endl
            << has_two_argument_foo<G>::value << std::endl
            << has_two_argument_foo<H>::value << std::endl
    ;
}


--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra

Dieser Post wurde am 09.07.2010 um 15:19 Uhr von 0xdeadbeef editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
09.07.2010, 15:44 Uhr
ex-bart



Vielen Dank!

Das ist schon ziemlich gut. Es gibt allerdings ein problem mit member function pointers, nämlich kann es in meinem Fall durchaus vorkommen, das foo() ein template ist:

C++:
struct H { template<typename T> void foo(T, T) { } };


Dann funktioniert es wieder nicht, weil der member function pointer mehrdeutig ist und erst gecasted werden muß:

C++:
template<typename T>
struct has_two_argument_foo<T, typename sfinae_helper<sizeof(sfinae_memfunc_helper(static_cast<void (T::*)(int,int)>(&T::foo))(0, 0))>::type> {
  static bool const value = true;
};


Das wiederum führt aber dazu, das wieder nur exact void foo(int,int) erkannt wird und z.B. int foo(int,int) oder void foo(double,double) nicht. Aber ich vermute, damit kann ich leben
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
09.07.2010, 15:54 Uhr
0xdeadbeef
Gott
(Operator)


Das wäre auch schwierig; eine Funktionsvorlage ist ja keine Funktion, und es ist schwer abzusehen, welche Funktionen letztendlich im Programm benutzt werden. Man liefe wohl in verschiedene Henne/Ei-Probleme.

Jedenfalls kommt man da mit dieser Technik nicht weiter, denn auf eine Vorlage kann man keinen Zeiger nehmen.

Vielleicht solltest du in so einem Fall schlicht auf Type-Traits zurückgreifen.
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra

Dieser Post wurde am 09.07.2010 um 15:55 Uhr von 0xdeadbeef editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
09.07.2010, 16:20 Uhr
ex-bart



OK, ich glaube ich habe es. Mit Hilfe von sizeof kann ich den die Größe der
meisten Typen bestimmen; die große Ausnahme ist void. Das kann ich aber
wiederum durch verwenden des Komma-Operators umgehen, einer der wenigen
Operatoren, deren Operanden den Typ void haben dürfen:

C++:
sizeof((foo(0,0), 0))


(Die doppelten Klammern wären hier nicht nötig, das sizeof ein Operator und
keine Funktion ist, ich hab sie nur zum besseren Verständnis drinne)

Damit sieht das ganze so aus:

C++:
#include <cstddef>
#include <iostream>

struct false_type { };
struct true_type  { };

template<std::size_t> struct sfinae_helper { typedef true_type type; };

template<typename T, typename = true_type>
struct has_two_argument_foo {
  static bool const value = false;
};

template<typename T>
struct has_two_argument_foo<T, typename sfinae_helper<sizeof((((T*)0)->foo(0,0) ,0))>::type> {
  static bool const value = true;
};

struct A { };
struct B { int  foo(int, int) { } };
struct C { void foo(int, int) { } };
struct D { void foo(int) { } };

struct E { int  foo(false_type, true_type) { } };
struct F { void foo(false_type, true_type) { } };

struct G { int  foo(double, double) { } };
struct H { template<typename T> void foo(T, T) { } };

int main() {
  std::cout << has_two_argument_foo<A>::value << std::endl
            << has_two_argument_foo<B>::value << std::endl
            << has_two_argument_foo<C>::value << std::endl
            << has_two_argument_foo<D>::value << std::endl
            << has_two_argument_foo<E>::value << std::endl
            << has_two_argument_foo<F>::value << std::endl
            << has_two_argument_foo<G>::value << std::endl
            << has_two_argument_foo<H>::value << std::endl
    ;
}

 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
007
09.07.2010, 16:56 Uhr
0xdeadbeef
Gott
(Operator)


Clever. Das gefällt mir.

Obwohl ich von den alten C-Casts dringend abrate.
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra

Dieser Post wurde am 09.07.2010 um 16:57 Uhr von 0xdeadbeef editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: