Вопрос

I hope that this question isn't overly convoluted. I realize that meta-programming acts on types rather than on the objects of those types; however, I am still trying to achieve the same result, by 1) retrieving the type information from the class and then 2) meta-functions on that type information.

An explanation of my situation is as follows with simplified code excerpts:

I have a template class for matrices, which I am calling Matrix_Base. Somewhat similar to the approach taken by Eigen, I am allowing for two possibilities for the size of a matrix -- either fixed at compile-time or fixed at run-time. Simplified declaration of Matrix_Base is:

template <typename Type, uint32_t rows_ = 1, uint32_t cols_ = 1,>
class Matrix_Base{
/* 
...
*/
};

Run-time sized matrix is denoted by an argument of 0.

The check for run-time vs compile-time sizing of the matrix is fairly simple (using boost::mpl):

            typedef typename mpl::if_<
                typename mpl::or_<
                typename mpl::equal_to<
                typename mpl::int_<rows_>::type,
                mpl::int_<0>
                >::type,
                typename mpl::equal_to <
                typename mpl::int_<cols_>::type,
                mpl::int_<0>
                >
                >::type,
                mpl::true_,
                mpl::false_
                >::type runtime_size_type;

This has been tested and works fine. My trouble starts about here...

Presently, I am using the above boost::mpl code in the following manner:

namespace internal {
template <uint32_t rows = 1, uint32_t cols_ = 1>
struct Runtime_Size_Helper {
typedef typename mpl::if_< 
// REST OF THE BOOST::MPL code here //
>::type runtime_size_t
bool value() { return runtime_size_t::value;}
};
} // namespace
template <typename Type, uint32_t rows_ = 1, uint32_t cols_ = 1>
class Matrix_Base{
// ...
static constexpr uint32_t rows = rows_;
static constexpr uint32_t cols = cols_;
bool is_runtime_sized;
// ...
};

template <typename T, uint32_t R, uint32_t C>
bool Matrix_Base<T,R,C>::is_runtime_sized = internal::Runtime_Size_Helper<R,C>::value();

This makes the result of that mpl function into a member of the Matrix_Base class. So far so good.

I'd like to use some form of indirection to determine the value of runtime_size_type by passing it the instantiated object. As per the example code, the only required information to determine this is the uint32_t parameters for col and row.

For the instantiated Matrix_Base object, the relevant information will never change from its compile-type values. The size of the matrix will be immutable; the size will either be set from the template arguments or -- for runtime-sized matrices -- through the constructor. In both cases, the template arguments are fixed and part of the type information. I am saving this information as static variables in the class, and I have even tried to add a typedef with all of the template parameters as my_type typename typename Matrix_Base<T,rows_, cols_, ...> my_type but I cannot seem to figure out how to write a metafunction to which I can pass a Matrix_Base object (obviously as a reference or pointer) and re-extract the relevant information.

I am fully open to incorporating (other) boost libraries, if they would provide the necessary functionality.

Hope that this is clear. Please let me know if there's something here that's unclear or that's just plain stupid.

Best regards, Shmuel

edited the text to provide a bit more clarity as to the issue

Это было полезно?

Решение

The quickest way to do what you seem to want (you didn't really specify it in detail) is to let your matrix class template inherit from a storage class template, and to specialize the storage class depending on whether your MPL predicate returns true or false

#include <array>
#include <iostream>
#include <vector>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/comparison.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/logical.hpp>

using namespace boost;

// in C++98, use 
// template<int R, int C>
// struct is_runtime_sized: mpl::if<
// ...
// >::type {};
template<int R, int C>
using is_runtime_sized = typename mpl::if_<
    mpl::or_<
        mpl::equal_to<mpl::int_<R>, mpl::int_<0>>,
        mpl::equal_to<mpl::int_<C>, mpl::int_<0>>
    >,
    mpl::true_, mpl::false_
>::type;

Note that I've eleminated some unnecessary typename occurances to make the MPL predicate more readable.

template<class T, int R, int C, bool = is_runtime_sized<R, C>::value>
struct MatrixStorage
{
    MatrixStorage() = default;
    MatrixStorage(int r, int c): data_(r * c) {} // zero-initializes
protected:    
    std::vector<T> data_;
};

template<class T, int R, int C>
struct MatrixStorage<T, R, C, false>
{
    MatrixStorage() = default;
    MatrixStorage(int, int): data_{} {} // zero-initializes
protected:    
    std::array<T, R * C> data_;    
};

Here, I've split the implementation of the dynamically and statically stored matrices. The former uses a std::vector and the latter a std:array. Similar to Eigen, both have a default constructor, and both also have a constructor taking the matrix dimensions that zero-initializes.

template<class T, int R = 0, int C = 0>
struct Matrix: public MatrixStorage<T, R, C>
{
    Matrix() = default;
    // In C++98, write:
    // Matrix(int r, int c): MatrixStorage<T, R, C>(r, c) {}
    using MatrixStorage<T, R, C>::MatrixStorage;
    int size() const { return this->data_.size(); }
};

The actual Matrix class inherits from the MatrixStorage, and returns a size() depending on the currently stored data.

int main()
{
    Matrix<int> m_dyn(3, 3);
    std::cout << m_dyn.size() << "\n"; // 9

    Matrix<int, 2, 2> m_stat;
    std::cout << m_stat.size() << "\n"; // 4
}

Live Example. As you can see, the dynamically allocated matrix has size 9, and the statically sized one has size 4. Note that there are two small C++11 features in the above code, that you can easily work-around if you are required to use C++11.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top