динамически вызывать одноименную функцию с другим типом возврата - PullRequest
0 голосов
/ 21 октября 2019

У меня есть ситуация здесь ... Я хочу спроектировать Фабрику, где я мог бы вызывать функцию с тем же именем и без параметров, но возвращать разные типы данных. На основе SubClassName мне нужно создать экземпляр объекта.

Нужна помощь или руководство по любому шаблону проектирования, чтобы следовать?

РЕДАКТИРОВАТЬ: абстрактный псевдокод ...

class parent{
 public:
  virtual string getName() = 0;
  //some virtual function.. not sure how to design. As the return type is dynamic.
  *** getValue(){}
};
class A : public parent{
  int x;
 public:
  virtual string getName(){ return "A";}
  virtual int getValue(){retun x;}
};
class B : public parent{
  string s;
 public:
  virtual string getName(){ return "B";}
  virtual string getValue(){ return s;}
};

void main(){
  string callingClass = "B";
  parent * arrayPtrs[2];
  arrayPtrs[0] = new A;
  arrayPtrs[1] = new B;

  for (loop through array, through iterator i){
  if(arrayPtrs[i]->getName == callingClass ){
     cout<<arrayPtrs[i]->getValue;
   }
  }
}

1 Ответ

0 голосов
/ 21 октября 2019

В C ++ функция может иметь только один возвращаемый тип за раз, и вы не можете изменить это динамически.
Однако - как предлагает @mch - вы можете использовать специализации шаблонов. Имейте в виду, что этот метод не является динамическим . Ваши функции будут сгенерированы в время компиляции .

Если я правильно понял ваш вопрос, может быть, это поможет.

class MyObject1
{
    //...
};
class MyObject2
{
    //...
};

template<typename T>
struct Factory
{
    constexpr static T gen();
};

template<>
struct Factory<MyObject1>
{
    constexpr static MyObject1 gen()
    {
        return MyObject1(/*... whatever parameters you see fit ...*/);
    }
};

template<>
struct Factory<MyObject2>
{
    constexpr static MyObject2 gen()
    {
        return MyObject2(/*... whatever parameters you see fit ...*/);
    }
};


int main()
{
    auto myObj = Factory<MyObject1>::gen();
    return 0;
}



Хотя этот метод мне кажется довольно бесполезным. Вы можете просто вызвать нужный конструктор вместо этого.
Но опять же, я не уверен, что это то, о чем вы подумали. Если я допустил какие-либо ошибки, пожалуйста, поправьте меня. Я постараюсь отредактировать свой ответ как можно лучше.

РЕДАКТИРОВАТЬ:
Чтобы сохранить виртуальную функциональность, я могу думать только о стирании типа: см. https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Type_Erasure

БлижайшийЯ мог бы получить то, что вы просили, это:

#include <iostream>
#include <string>
#include <any>


class parent {
public:
    // you can use this too but I think type checking is more handy
    // see in main function
    /* virtual std::string getName() const = 0; */

    virtual std::any getValue() const = 0;
};
class A : public parent {
public:
    typedef int value_type;

private:
    value_type x;
public:
    A(value_type x) :
        x(x)
    {}

    /* virtual std::string getName() const override { return "A"; } */
    virtual std::any getValue() const override
    { return this->x; }
};
class B : public parent {
public:
    typedef std::string value_type;

private:
    value_type s;

public:
    B(const value_type& s) :
        s(s)
    {}

    /* virtual std::string getName() const override { return "B"; } */
    virtual std::any getValue() const override
    { return this->s; }
};

int main(){
    using callingClass = A;
    parent* arrayPtrs[2];
    arrayPtrs[0] = new A(42);
    arrayPtrs[1] = new B("my string");

    for (unsigned i = 0; i < sizeof(arrayPtrs) / sizeof(parent*); ++i)
    {
        // Note:
        // dynamic cast will return nullptr if $callingClass
        // is not a derived class
        if (dynamic_cast<callingClass*>(arrayPtrs[i]))
            std::cout << std::any_cast<callingClass::value_type>(arrayPtrs[i]->getValue()) << std::endl;
    }


    return 0;
}



Надеюсь, это поможет.
Обратите внимание, что я использовал dynamic_cast, чтобы проверить правильный тип. Если вы знаете лучшее решение, вы можете использовать это тоже. Но в этих условиях я не мог придумать ничего лучшего.

РЕДАКТИРОВАТЬ 2:

#include <iostream>
#include <string>
#include <tuple>


class some
{
    using id = size_t;

    template<typename T>
    struct type { static void id() { } };

    template<typename T>
    static id type_id() { return reinterpret_cast<id>(&type<T>::id); }

    template<typename T>
    using decay = typename std::decay<T>::type;

    template<typename T>
    using none = typename std::enable_if<!std::is_same<some, T>::value>::type;

    struct base
    {
        virtual ~base() { }
        virtual bool is(id) const = 0;
        virtual base *copy() const = 0;
    } *p = nullptr;


    template<typename T>
    struct data : base, std::tuple<T>
    {
        using std::tuple<T>::tuple;

        T       &get()      & { return std::get<0>(*this); }
        T const &get() const& { return std::get<0>(*this); }

        bool is(id i) const override { return i == type_id<T>(); }
        base *copy()  const override { return new data{get()}; }
    };

    template<typename T>
    T &stat() { return static_cast<data<T>&>(*p).get(); }

    template<typename T>
    T const &stat() const { return static_cast<data<T> const&>(*p).get(); }

    template<typename T>
    T &dyn() { return dynamic_cast<data<T>&>(*p).get(); }

    template<typename T>
    T const &dyn() const { return dynamic_cast<data<T> const&>(*p).get(); }

public:
     some() { }
    ~some() { delete p; }

    some(some &&s)      : p{s.p} { s.p = nullptr; }
    some(some const &s) : p{s.p->copy()} { }

    template<typename T, typename U = decay<T>, typename = none<U>>
    some(T &&x) : p{new data<U>{std::forward<T>(x)}} { }

    some &operator=(some s) { swap(*this, s); return *this; }

    friend void swap(some &s, some &r) { std::swap(s.p, r.p); }

    void clear() { delete p; p = nullptr; }

    bool empty() const { return p; }

    template<typename T>
    bool is() const { return p ? p->is(type_id<T>()) : false; }

    template<typename T> T      &&_()     && { return std::move(stat<T>()); }
    template<typename T> T       &_()      & { return stat<T>(); }
    template<typename T> T const &_() const& { return stat<T>(); }

    template<typename T> T      &&cast()     && { return std::move(dyn<T>()); }
    template<typename T> T       &cast()      & { return dyn<T>(); }
    template<typename T> T const &cast() const& { return dyn<T>(); }

    template<typename T> operator T     &&()     && { return std::move(_<T>()); }
    template<typename T> operator T      &()      & { return _<T>(); }
    template<typename T> operator T const&() const& { return _<T>(); }
};


using any = some;


class parent {
public:
    // you can use this too but I think type checking is more handy
    /* virtual std::string getName() const = 0; */

    virtual any getValue() const = 0;
};
class A : public parent {
public:
    typedef int value_type;

private:
    value_type x;
public:
    A(value_type x) :
        x(x)
    {}

    /* virtual std::string getName() const override { return "A"; } */
    virtual any getValue() const override
    { return this->x; }
};
class B : public parent {
public:
    typedef std::string value_type;

private:
    value_type s;

public:
    B(const value_type& s) :
        s(s)
    {}

    /* virtual std::string getName() const override { return "B"; } */
    virtual any getValue() const override
    { return this->s; }
};

int main(){
    using callingClass = A;
    parent* arrayPtrs[2];
    arrayPtrs[0] = new A(42);
    arrayPtrs[1] = new B("my string");

    for (unsigned i = 0; i < sizeof(arrayPtrs) / sizeof(parent*); ++i)
    {
        // Note:
        // dynamic cast will return nullptr if $callingClass
        // is not a derived class
        if (dynamic_cast<callingClass*>(arrayPtrs[i]))
            std::cout << arrayPtrs[i]->getValue()._<callingClass::value_type>() << std::endl;
    }


    return 0;
}

Этот фрагмент кода на случай, если вы не можете использовать функции C ++ 17, и основан на:
любого класса

...