Search code examples
c++templatestemplate-specializationvmat

What is a better way to specialize this template function for int8_t and uint8_t?


Consider the template dump function below:

namespace {

    using namespace Eigen;
    using namespace std;
    using namespace vMAT;

    template <typename T>
    NSString *
    dump(NSString * prefix, T * A, vMAT_Size sizeA)
    {
        NSMutableString * dump = [NSMutableString stringWithString:prefix];
        [dump appendString:@" = \n"];
        Eigen::Map<Matrix<T, Dynamic, Dynamic>> DATA(A, sizeA[0], sizeA[1]);
        stringstream out;
        out << DATA << endl;
        [dump appendFormat:@"%s", out.str().c_str()];
        return dump;
    }

    template <> // Specialized so elements print as numbers instead of chars
    NSString *
    dump(NSString * prefix, int8_t * A, vMAT_Size sizeA)
    {
        NSMutableString * dump = [NSMutableString stringWithString:prefix];
        [dump appendString:@" = \n"];
        Eigen::Map<Matrix<int8_t, Dynamic, Dynamic>> DATA(A, sizeA[0], sizeA[1]);
        stringstream out;
        out << DATA.cast<int32_t>() << endl;
        [dump appendFormat:@"%s", out.str().c_str()];
        return dump;
    }

    template <> // Specialized so elements print as numbers instead of chars
    NSString *
    dump(NSString * prefix, uint8_t * A, vMAT_Size sizeA)
    {
        NSMutableString * dump = [NSMutableString stringWithString:prefix];
        [dump appendString:@" = \n"];
        Eigen::Map<Matrix<uint8_t, Dynamic, Dynamic>> DATA(A, sizeA[0], sizeA[1]);
        stringstream out;
        out << DATA.cast<uint32_t>() << endl;
        [dump appendFormat:@"%s", out.str().c_str()];
        return dump;
    }

}

As you can see, I've copy/paste/edited it to specialize how int8_t and uint8_t matrices are dumped. But this is exactly the sort of madness that templates are supposed to eliminate!

I have tried to add an additional template typename AsT parameter to the original function, but keep running afoul of the compiler complaining about this line:

out << DATA.cast<AsT>() << endl;

Xcode complains that the cast<AsT>() is a "dependent template" and wants to insert the template keyword in front of it… Which seems to be nonsensical syntax that then generates yet another compiler error.

What is a better way to specialize this template function for int8_t and uint8_t?


Solution

  • Create another function template which is designed to only work with uint8_t and/or int8_t, and have your specializations of dump call that other function template.

    Alternately, just create overloads of functions called from the function template dump. Here's an example demonstrating how that works: http://ideone.com/Z88DU6

    I believe doing that to your code would look something like the following, but I'm not entirely positive this works given that I have never used Objective-C (or Objective-C++).

    // Note: I believe this works but am not a template wizard and have not tried
    namespace {
    
        using namespace Eigen;
        using namespace std;
        using namespace vMAT;
    
        template <typename T>
        inline Eigen::Map<Matrix<T, Dynamic, Dynamic>> get_printable_eigen(Eigen::Map<Matrix<T, Dynamic, Dynamic>> const& in)
        {
            return in;
        }
    
        // No specialization; simple overloads!
        inline Eigen::Map<Matrix<int32_t, Dynamic, Dynamic>> get_printable_eigen(Eigen::Map<Matrix<int8_t, Dynamic, Dynamic>> const& in)
        {
            return in.cast<int32_t>();
        }
    
        inline Eigen::Map<Matrix<uint32_t, Dynamic, Dynamic>> get_printable_eigen(Eigen::Map<Matrix<uint8_t, Dynamic, Dynamic>> const& in)
        {
            return in.cast<uint32_t>();
        }
    
        template <typename T>
        NSString *
        dump(NSString * prefix, T * A, vMAT_Size sizeA)
        {
            NSMutableString * dump = [NSMutableString stringWithString:prefix];
            [dump appendString:@" = \n"];
            Eigen::Map<Matrix<T, Dynamic, Dynamic>> DATA(A, sizeA[0], sizeA[1]);
            stringstream out;
            out << get_printable_eigen(DATA) << endl;
            [dump appendFormat:@"%s", out.str().c_str()];
            return dump;
        }
    }
    

    (There are probably other solutions using enable_if and type traits but SFINAE should be reserved for cases which cannot otherwise be accomplished through simpler means because it produces poor error messages)