1

I have a situation that occurs 5 times in my code, and continues to grow. Basically I have the same switch statement structure every time I need to perform an operation that involves my matrix and its type (which is set dynamically at runtime from files and user input). Here is one such example:

//QTableWidget m_matrix_table;
//cv::Mat m_matrix;
MatrixBoxWidget::MatrixBoxWidget(QWidget *parent) : QWidget(
        parent) {
     ...
     connect(m_matrix_table, &QTableWidget::cellChanged, this,
             &MatrixBoxWidget::updateMatrixValue);
     ...
}

void MatrixBoxWidget::updateMatrixValue(int row, int column) {
    QTableWidgetItem item = m_matrix_table->item(row, column);
    QVariant qvariant = item->data(0);
    int type = m_matrix.type();
    switch (type) {
        case CV_8U:
            m_matrix.at<std::uint8_t>(row, column)
                    = qvariant.value<std::uint8_t>();
            break;
        case CV_8S:
            m_matrix.at<std::int8_t>(row, column)
                    = qvariant.value<std::int8_t>();
            break;
        case CV_16U:
            m_matrix.at<std::uint16_t>(row, column)
                    = qvariant.value<std::uint16_t>();
            break;
        case CV_16S:
            m_matrix.at<std::int16_t>(row, column)
                    = qvariant.value<std::int16_t>();
            break;
        case CV_32S:
            m_matrix.at<std::int32_t>(row, column)
                    = qvariant.value<std::int32_t>();
            break;
        case CV_32F:
            m_matrix.at<float>(row, column)
                    = qvariant.value<float>();
            break;
        case CV_64F:
            m_matrix.at<double>(row, column)
                    = qvariant.value<double>();
            break;
        default:
            assert((false, "unknown type format"));
            break;
    }

}

usually the function signatures (or what ever else I decide to do in the switch cases) are homogenous except for the types in most circumstances, I'm only aiming to fix these cases.

Opencv uses these integers to change the type of a matrix. This is what I'm switching on. What can I do to mitigate this? Or is this kind of boiler plate necessary? Template functions that take another function don't appear to work since they would require all the templates versions of the function anyway, which doesn't get rid of the boiler plate code.

EDIT:

The marked duplicate has actually zero to do with my question. There's not a single template mentioned in either answer, and neither solves my problem.

This is what I'd like to do:

foo<T>(int type, std::function bar) where bar would be templated inside of foo, but that is clearly not possible with current C++ semantics. the problem isn't that I'm using switch statements, its that the pattern is homogeneous across multiple use cases with the same cases.

  • 2
    @gnat most definitely not, the fact that it has switch in the name doesn't mean it helps me (the answers are completely irrelevant to my needs) though it would be nice if there was a duplicate. I'm not looking to put a giant class hierarchy to "solve" this that would just make the code not clean. I need away to have the structure of the switch statement, and also input templated functions into ideally like so: foo<T>(type, bar) where bar is templated inside of foo. – opa Jul 27 '18 at 15:22
  • 1
    Can you template you whole code (or at least everything that's called once you know the type you are using)? – Caleth Jul 27 '18 at 15:25
  • @Caleth No. the type is only known at runtime, and all the functions that are called with these switch statements are called at runtime, the type can only be known at runtime (I mention this previously, because its defined through UI via user manually selecting type or by loading a separate file). For example, there's a UI function to update the internal matrix with the display widget In order to update the matri I need to set values in the opencv matrix. The only way to set values from a opencv matrix is to also know the type along side it, it is a templated function (at<T>) – opa Jul 27 '18 at 15:30
  • 1
    What I mean is, you could reduce it from 5 places to one place. int main() { auto type = getType(); switch (type) { case CV_8U: new_main<uint_8t>(); ... } }; – Caleth Jul 27 '18 at 15:34
9

Well, if you have the same kind of code multiple times, meaning switch on type-code and then use identical code with that type, how about using a template and generic lambda?

template <class F>
auto do_typed(int type, F f) {
    switch(type) {
    case CV_8U:  return f(std::enable_if<true, std::uint8_t>());
    case CV_8S:  return f(std::enable_if<true, std::int8_t>());
    case CV_16U: return f(std::enable_if<true, std::uint16_t>());
    case CV_16S: return f(std::enable_if<true, std::int16_t>());
    case CV_32S: return f(std::enable_if<true, std::int32_t>());
    case CV_32F: return f(std::enable_if<true, float>());
    case CV_64F: return f(std::enable_if<true, double>());
    }
    assert(false);
}

And use it like:

do_typed(m_matrix.type(), [&](auto x){
    using T = typename decltype(x)::type;
    m_matrix.at<T>(row, column) = qvariant<T>();
});

I'm using the trivial type std::enable_if<true, T> to signal the intended type because it is always a trivial empty type, thus not imposing any restrictions on what can be conveyed.

Also, if you have more type-lists and the type-codes are consecutive, it can be easily generalized.

  • Oh shit, I didn't even know this was possible, I think this is exactly what I was looking for. – opa Jul 27 '18 at 15:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.