Sprawdzenie typu podczas kompilacji

0

Cześć, szukam i nie mogę znaleźć czegoś takiego.

Mam funkcję:

template<class itemClass>
void foo(itemClass & item)
{
  bool is_class_a = boost::is_same<itemClass, ClassA>::value; //sprawdzam, czy itemClass to klasa ClassA

  if(is_class_a) item.bar2();
  item.bar();
}

I teraz tak. Wszystkie klasy, jakie przekazuję w templacie mają metodę bar(), ale tylko jedna(ClassA) ma bar2(). Niestety nie chce mi to przejść kompilacji. Błąd jest taki, że nie ma metody bar2().

Czyli rodzi się pytanie: Jak podczas kompilacji sprawdzić typ przekazanej klasy?

1

Jeśli musisz tak zrobić to znaczy że coś bardzo mocno skaszaniłeś projektując to rozwiązanie.

Ale tak, da się to zrobić. Możesz zrobić dynamic_cast i zobaczyć jak zareaguje.

3

I teraz tak. Wszystkie klasy, jakie przekazuję w templacie mają metodę bar(), ale tylko jedna(ClassA) ma bar2(). Niestety nie chce mi to przejść kompilacji. Błąd jest taki, że nie ma metody bar2().

A nie chodzi Ci o coś takiego?:

#include <iostream>
using namespace std;

class ClassA { 
public:
    void bar() { cout << "bar" << endl; }
    void bar2() { cout << "bar2" << endl; }
};

class ClassB { 
public:
    void bar() { cout << "bar" << endl; }
};

template <class itemClass>
void foo(itemClass &item) {
  item.bar();
}

template <>
void foo<ClassA>(ClassA &item) {
  item.bar2();
  item.bar();
}

int main() {
    ClassA a;
    ClassB b;

    foo(a);
    foo(b);
}
$ ./a.exe
bar2
bar
bar

(Nie żeby to był dobry pomysł)

0

Chyba nie zrozumiesz działania szablonów. Jeśli sprawdzisz podczas kompilacji, czy parametr szablony jest typu T (std::/boost::is_same) to nadal nie ogranicza (nie usuwa) kodu, który napisałeś w tym szablonie. Czytaj, nadal będziesz próbował wywoływać metody, których dana klasa może nie zawierać.
Jedyne sensowne rozwiązanie w tym przypadku (specjalizacja szablonu) zaprezentował już ci @MSM.
Jednakże tak jak wspomniał @Shalom, najprawdopodobniej sknociłeś coś przy projektowaniu.
@Shalom - dynamic_cast nie działa w czasie kompilacji tylko w runtime.

3

może lekko przegiąłem, ale siedziałem teraz nad tym ze 3 godziny więc wkleję co wymodziłem :D

ten kod sprawdza czy podany typ posiada zdefiniowanego T insert(Args...);

#include <iostream>
#include <type_traits>
#include <vector>
#include <set>

template<typename, typename T>
struct has_insert_method
{
    static_assert(std::integral_constant<T, false>::value,
                  "Second template parameter needs to be of function type.");
};

template<typename C, typename Ret, typename... Args>
struct has_insert_method<C, Ret(Args...)>
{
private:
    template<typename T>
    static constexpr auto check(T*) -> typename std::is_same<decltype(std::declval<T>().insert(std::declval<Args>()...)), Ret>::type;

    template<typename>
    static constexpr std::false_type check(...);

    typedef decltype(check<C>(0)) type;

public:
    static constexpr bool value = type::value;
};

int main()
{
    typedef std::vector<int> v;
    std::cout << "Does vector have an insert method?: " << has_insert_method<v, std::pair<v::iterator, bool>(v::value_type const&)>::value;
    std::cout << std::endl;

    typedef std::set<int> t;
    std::cout << "Does set have an insert method?: " << has_insert_method<t, std::pair<t::iterator, bool>(t::value_type const&)>::value;

    return 0;
}

http://ideone.com/4r8hFt

0

@gośćabc wtf? na to są już gotowe rozwiązania, jak np. idiom SFINAE albo https://github.com/jaredhoberock/is_call_possible

0

OK, w końcu zrobiłem inaczej. Obie funkcje byłyby takie same, tylko różniłyby się tym jednym wywołaniem. Więc posłużyłem się zwykłym rzutowaniem:


if(is_class_a)
    (ClassA*)item->bar2();

Trochę to chamizacja, ale działa.

0

OK, to powiem dokładnie o co mi chodzi. Mam dwie klasy. Jedna ma pola:

ParentClass:


wstring m_name;
double m_limit;

a druga (dziedzicząca po tej pierwszej) dodatkowo ma pole:
DerivedClass:


double m_amount;

Dostęp do pól jest realizowany za pomocą metod. I teraz chodzi o to, że chcę napisać metodę (poza tymi klasami), która pokaże mi formę z dwoma lub trzema editami, a potem tak pobrane dane odpowiednio umieści w obiektach.

Teraz robię to mniej więcej tak:


wstring name;
double limit;
double amount;

if(isParentClass)
  GetData(name, limit); //przekazywane przez referencję
else
  GetData(name, limit, amount);

GetData() pokazuje mi odpowiednią formę i zwraca mi wpisane przez użytkownika wartości. Następnie po prostu przypisuję je do obiektu. To teraz, proszę, powiedzcie mi jak to zrobić lepiej bez duplikowania kodu i chamizacji, jaką zrobiłem z tym rzutowaniem :)

0

@MSM już podał ci dwa, możliwe rozwiązania: specjalizacja szablonu i strategy pattern. Do szablonu możesz jeszcze dodać sprawdzenie, czy typ szablonu jest pochodną klasy.

#include <iostream>
#include <type_traits>

struct A { int a; };
struct B : public A { int b; };
struct C { float c; };

template <typename T>
void GetData(T& t, std::istream& in = std::cin)
{
    static_assert(std::is_base_of<A, T>::value, "T must derived from A!");
    in >> t.a;
}

template <>
void GetData<B>(B& b, std::istream& in)
{
    in >> b.a >> b.b;
}

int main() 
{
    A a;
    B b;
    C c;

    GetData(a);
    GetData(b);
    //GetData(c);  tutaj poleci assercja

    std::cout << a.a << std::endl;
    std::cout << b.a << " " << b.b << std::endl;

    return 0;
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0