CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 4 of 4
  1. #1
    Lindley is offline Elite Member Power Poster
    Join Date
    Oct 2007
    Location
    Seattle, WA
    Posts
    10,895

    Avoiding template parameter creep

    Let's say you decide you want a std::array as a member of your class.

    std::array takes its size and type as template parameters. Therefore, the outer class must either determine these explicitly, or else also take them as template parameters.

    However, it may be the case that your interface has no dependency on the size of the internal std::array. Therefore, it would be desirable not to need to keep on specifying it all over the place. In fact, the only place the size really matters is during object construction.

    Well then, you ask, why not just use a std::vector instead of a std::array?

    In this trivial case you could. But in the general case, that kind of change may not be possible. Say, for instance, you're depending on 3rd-party code that has a similar template parameter and you can't change it.

    Well then, why not derive your class from an interface that doesn't care about the extra template parameter?

    Perhaps that's a solution, but it would add a virtual function call penalty to everything you do with the class. That's fine most of the time, but in a performance-critical case it may not be desirable.

    The question is, does anyone know a pattern for "shrugging off" template parameters from contained types that you don't care about in the entire interface, only a limited subset of it, that doesn't prevent compiler inlining and other optimizations the way virtual function calls can?

  2. #2
    Join Date
    Jul 2005
    Location
    Netherlands
    Posts
    2,042

    Re: Avoiding template parameter creep

    You can create a nested wrapper class that derives from some base class to which you store a pointer. Something like this (untested code):
    Code:
    #include <array>
    #include <memory>
    
    struct Test
    {
      template <size_t N>
      Test(const std::array<int, N>& arr)
        : p(std::make_shared<Array<N>>(arr))
      {}
    
      template <size_t N>
      void usesInternalArray()
      {
        assert(dynamic_cast<Array<N>*>(p.get()));
        thirdPartyFunction(static_cast<Array<N>*>(p)->arr);
      }
    
    private:
      struct ArrayBase
      {
        virtual ~ArrayBase() {}
      };
      template <size_t N>
      struct Array : public ArrayBase
      {
        Array(const std::array<int, N>& arr) : arr(arr) {}
        std::array<int, N> arr;
      };
      std::shared_ptr<ArrayBase> p;
    };
    
    int main()
    {
      std::array<int, 4> arr1;
      std::array<int, 9> arr2;
      Test test1(arr1), test2(arr2);
    }
    You can avoid the derivation from the base class and use a std::shared_ptr<void> if you can live without the runtime
    check of the dynamic_cast in usesInternalArray() and basically just have the program blow up if the user calls the function with a different template argument that was used for the c'tor.
    Cheers, D Drmmr

    Please put [code][/code] tags around your code to preserve indentation and make it more readable.

    As long as man ascribes to himself what is merely a posibility, he will not work for the attainment of it. - P. D. Ouspensky

  3. #3
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: Avoiding template parameter creep

    or an (automatic) trait.

    it really depends on how the value for the array-size is determined.

    you could do something like this if the size is determined purely by the template parameter type;

    Code:
    class myclass
    {
    public:
    	static const size_t size_for_array = 8;
    };
    template <typename T>
    class mycontainer
    {
    protected:
    	T m;
    	std::array<int, T::size_for_array> a;
    };
    with some extra work you can also make this work with "externally" defined sizes. in case you can't stuff a size-member in the class.

    Code:
    // class with internally defined size
    class myclass
    {
    public:
    	static const size_t size_for_array = 8;
    };
    // class without internally defined size
    class myclass2
    {
    };
    
    // helper template class to get size from any class, default implementation assumes internally defined size
    template<typename T>
    class getsizefromclass
    {
    public:
    	static const size_t size_for_array = T::size_for_array;
    };
    
    // specialize helper template for the class without internally defined size
    template<>
    class getsizefromclass<myclass2>
    {
    public:
    	static const size_t size_for_array = 5;
    };
    
    
    template <typename T>
    class mycontainer
    {
    public:
    	T m;
    	std::array<int, getsizefromclass<T>::size_for_array> a;
    };
    
    void foo()
    {
    	mycontainer<myclass> mcc; // 8
    	mycontainer<myclass2> mcc2; // 5
    
    }
    there's loads of alternatives depending on what exactly will determine the size of the array. Note, you will need to have it defined at compiletime, you can't define an std::array at runtime.
    Last edited by OReubens; November 21st, 2013 at 09:31 AM.

  4. #4
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: Avoiding template parameter creep

    or take traits one step further having those define the type and size.

    Code:
    class myclass
    {
    };
    
    
    class myclass8trait
    {
    public:
    	typedef myclass	data_type;
    	static const size_t size_for_array = 8;
    };
    class myclass14trait
    {
    public:
    	typedef myclass	data_type;
    	static const size_t size_for_array = 14;
    };
    class int88trait
    {
    public:
    	typedef int	data_type;
    	static const size_t size_for_array = 88;
    };
    
    template <typename T>
    class mycontainer
    {
    public:
    	typename T::data_type m;
    	std::array<typename T::data_type, T::size_for_array> a;
    };
    
    void foo()
    {
    	mycontainer<myclass8trait> mc8;
    	mycontainer<myclass14trait> mc14;
    	mycontainer<int88trait> i88;
    }
    Last edited by OReubens; November 21st, 2013 at 09:37 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured