开发者

How to bypass template virtual function to attain my goal?

开发者 https://www.devze.com 2023-03-16 17:31 出处:网络
I know this isn\'t legal C++ due to the compiler not being able to determine how big exactly the vtable is. I\'m looking for alternatives.

I know this isn't legal C++ due to the compiler not being able to determine how big exactly the vtable is. I'm looking for alternatives.

Basically, I have 开发者_运维技巧an abstract base class defining the interface for a set of derived classes. The return values of some of the functions being exposed through this interface are determined by the input parameter sKeyName. My intended code is posted below.

class DataAccess
{ 
    public: 
        template <class T> 
        virtual const Array2D<T>* GetData(std::string sKeyName) const = 0;

        template <class T>  
        virtual Array2D<T>* GetData(std::string sKeyName) = 0; 
}; 

Can somebody give me a workaround to get this functionality? Any help is appreciated. Thank you in advance.


What you'd need to do is define a separate, templated interface.

class DataAccess {
    template<typename T> class InnerInteface {
        virtual Array2d<T>* GetData(std::string) = 0;
    };
};
class OHai : public DataAccess, DataAccess::InnerInterface<float> {
};
int main() {
    DataAccess* D = new OHai;
    if (DataAccess::InnerInterface<float>* data = 
       dynamic_cast<DataAccess::InnerInterface<float>>(D)) {
    }
}

This might get you what you need. You can also template the base class, but I'm guessing that's not allowed.


You are asking about dynamic functions, but your main problem is not there, but rather here:

The return values of some of the functions being exposed through this interface are determined by the input parameter sKeyName.

C++ is a statically typed language, and that means that you cannot have a return type for a function that is dependent on the value of the arguments. Ignoring for the time being inheritance, the code that you presented requires the user to determine the type of the array that is being returned independently of the arguments passed:

struct SimpleDataAccess {
   template <typename T>
   array2d<T>* get_data( std::string const & which ) {
      return new array2d<T>();
   }
};
int main() {
   SimpleDataAccess accessor;
   array2d<int> = accessor.get<int>( "int" ); // <int> at the place of call fixes
                                              // the return type, not "int" !
}

Now, if you are willing to live with that (i.e. the caller will know and set the return type) there are different ways of providing workarounds for your particular problem of the language not allowing templated virtual functions. The first thing that comes to mind is nice in that it also follows the NVI idiom (and shows its importance): Provide a non-virtual public templated accessor to the data, and implement it in terms of a fixed return type virtual function.

class DataAccessor {
    virtual Type get_data_impl( std::string const & ) = 0;
public:
    template <typename T>
    array2d<T>* get_data( std::string const & which ) {
       Type tmp = get_data_impl( which );
       return convert( tmp );
    }
};

Assuming that we can resolve what Type and convert are, we have a solution. This is a good example of the NVI idiom: the interface offered by users (public, non virtual) differs from the interface required to extensions (private, virtual). The two contracts differ, your users require you to provide a pointer to specific concrete array2d instantiation, but the language does not allow you to require that same contract from your extensions, but that is not a problem because they are different interfaces.

Now back to Type and convert. These two are related, and there are different approaches that you can follow. The simplest to implement would be having an array2d_base class from which all array2d<T> derive (by providing a virtual destructor you enable RTTI):

struct array2d_base {
   virtual ~array2d_base() {}
};
template <typename T>
class array2d : public array2d_base {
   // implementation
};
// Type == array2d_base*
// convert == dynamic_cast< array2d<T>* >
template <typename T>
array2d<T>* DataAccessor::get_data( std::string const & s ) {
   return dynamic_cast< array2d<T>* >( get_data_impl( s ) );
}

If you cannot extend or modify the array2d class, then you can achieve a similar result by means of type erasure. This will have the advantage of not requiring RTTI in array2d but only in the type erasing support. The simplest such implementation would be using boost::any in the inner interface:

// Type == boost::any
// convert == boost::any_cast< array2d<T>* >
template <typename T>
array2d<T>* DataAccessor::get_data( std::string const & s ) {
   boost::any tmp = get_data_impl(s);
   return boost::any_cast< array2d<T>* >( tmp );
}


I am not sure if this answers your question, but you can take a look into CRTP. Then you would do :

template< typename T >
struct B
{
  virtual Array2D<T>* GetData(std::string sKeyName) = 0; 
};

struct A : public B< int >
{
  virtual Array2D<int>* GetData(std::string sKeyName)
  {
    // implement
  }
};


  • Template functions cannot be virtual.
  • Array2D needs a template parameter. Even if type of template class is default, <> is needed.


Let all your different types, that can be returned from GetData, inherit from a base class and return a pointer to that class. Example:

class Data {};
class Array2D: public Data{};
class DataAccess
{ 
    public:
        virtual Data* GetData(std::string sKeyName) const = 0;
};
0

精彩评论

暂无评论...
验证码 换一张
取 消