Some advanced C++: Why does this constexpr stuff not work?

Hi you C++ experts out here. This is not a JUCE-specific question, but maybe someone has an idea. For some reasons discussed somewhere else, I’m building my own set of sample buffer classes. I’m using a lot of template stuff for this and I basically have four sample buffer classes, declared like this:

template <typename SampleType>
class TypeASampleBufferRealValued
{
   // all member functions you need here
{

template <typename SampleType>
class TypeASampleBufferComplexValued
{
   // all member functions you need here
{

template <typename SampleType>
class TypeBSampleBufferRealValued
{
   // all member functions you need here
{

template <typename SampleType>
class TypeBSampleBufferComplexValued
{
   // all member functions you need here
{

Now I’d like to be able to write templated DSP classes that accept some of this sample buffers. Lets say I have this one:

template <typename SampleType>
class MyDSPClass
{
    template <typename BufferType>
    void processBuffer (BufferType& buffer)
    {
         // here I want to check at compile time if for an example BufferType is either 
        // TypeASampleBufferComplexValued<SampleType> or TypeBSampleBufferComplexValued<SampleType>
    }
}

You see from my comment in the processBuffer function what I want to achieve. So I thought writing a little helper class like this could be helpful:

template <typename  SampleType>
struct IsSampleBuffer
{
    template <typename BufferType>
    static constexpr bool real (BufferType&)
    {
        return std::is_same<BufferType, TypeASampleBufferRealValued<SampleType>>::value ||
                std::is_same<BufferType, TypeBSampleBufferRealValued<SampleType>>::value;
    }

    template <typename BufferType>
    static constexpr bool complex (BufferType&)
    {
        return std::is_same<BufferType, TypeASampleBufferComplexValued<SampleType>>::value ||
                std::is_same<BufferType, TypeBSampleBufferComplexValued<SampleType>>::value;
    }

    template <typename BufferType>
    static constexpr bool complexOrReal (BufferType&)
    {
        return std::is_same<BufferType, TypeASampleBufferRealValued<SampleType>>::value    ||
                std::is_same<BufferType, TypeBSampleBufferRealValued<SampleType>>::value    ||
                std::is_same<BufferType, TypeASampleBufferComplexValued<SampleType>>::value ||
                std::is_same<BufferType, TypeBSampleBufferComplexValued<SampleType>>::value;
    }
};

I’d like to use it like that

template <typename SampleType>
class MyDSPClass
{
    template <typename BufferType>
    void processBuffer (BufferType& buffer)
    {
         static_assert (IsSampleBuffer<SampleType>::complex (buffer), "Only complex sample buffers supported");
    }
}

However clang tells me Error:(126, 65) static_assert expression is not an integral constant expression. I don’t really see why this expression could not be evaluated at compile time and therefore why this is not an integral constant expression. Did I miss anything? Can this be done better? Any constexpr gurus out here? Any help appreciated :wink:

Here’s some example code on Complier Explorer that should work.

2 Likes

Nice, thank you! I never read something like MyClass::template foo<Bar>() before. So is this a general way of passing a template parameter to a templated member function without deducting the template type from a function parameter? As I see you didn’t specify any higher C++ language standard trough compiler flags, so can I assume that this is part of the basic C++03 feature set?

Basically, yes. There’s more information in the link below (search the page for “::template”), but if ::template isn’t specified there then the next < gets interpreted as a less-than operator.

https://en.cppreference.com/w/cpp/language/dependent_name