0

I've got a template-heavy C++ library that figures out (at compile time) how to convert between different pixel formats (i.e. convert RGB-5-6-5 to RGB-8-8-8 and such).

At its core, it isolates a pixel's individual color channels and uses a simple set of hard-coded methods to widen or narrow each channel:

template<std::size_t FromBits, std::size_t ToBits>
class UnsignedBitAdjuster;

template<>
class UnsignedBitAdjuster<5, 8> {
  public: template<int TLowestSourceBitIndex, int TLowestTargetBitIndex, typename TPixel>
  static constexpr TPixel Adjust(TPixel original) {
    return (
      BitShift<TLowestSourceBitIndex - TLowestTargetBitIndex - 3>(original) |
      BitShift<TLowestSourceBitIndex - TLowestTargetBitIndex + 2>(original)
    );
  }
};

However, at the point of usage, GCC and clang both report the same error, whereas ICC and MSVC compile the code:

template<
  PixelFormat TSourcePixelFormat, PixelFormat TTargetPixelFormat,
  typename std::enable_if_t<
    (TSourcePixelFormat != TTargetPixelFormat) &&
    (!IsFloatFormat<TSourcePixelFormat>) &&
    (!IsFloatFormat<TTargetPixelFormat>)
  > * = nullptr
>
void ConvertPixel(
  const PixelTypeFromFormat<TSourcePixelFormat> *sourcePixel,
  PixelTypeFromFormat<TTargetPixelFormat> *targetPixel
) {
  typedef LargerPixelType<TSourcePixelFormat, TTargetPixelFormat> IntermediatePixelType;

  if constexpr(NeedConvertChannel1<TSourcePixelFormat, TTargetPixelFormat>) {
    // This compiles
    //constexpr std::size_t realMagic = 5;

    // This leads to a compilation error when calling Adjuster::Adjust<0, 0>(value)
    constexpr std::size_t realMagic = PixelFormatDescription<TSourcePixelFormat>::Channel1::BitCount;
    constexpr std::size_t strongMagic = 4;

    typedef UnsignedBitAdjuster<realMagic, strongMagic> Adjuster;
    std::size_t value = 0;
    std::size_t actual = Adjuster::Adjust<0, 0>(value);
  }
}

The reported error is:

<source>: In function 'void Test::ConvertPixel(Test::PixelTypeFromFormat<TSourcePixelFormat>*, Test::PixelTypeFromFormat<TTargetPixelFormat>*)':
<source>:99:48: error: expected unqualified-id before numeric constant
   99 |       std::size_t actual = Adjuster::Adjust<0, 0>(value);
      |                                                ^

Now an unqualified-id, as per the C++ standard, is a simple name, without a namespace in front of it, i.e. when declaring a local variable. GCC & clang seem to say that instead of the 0, they expect such a name here?

I'm having trouble understanding what exactly drives these two compilers to take a wrong turn and so far was unable to come up with a workaround.

Maybe my code really is at fault? (ICC and MSVC compile the code cleanly).

I have uploaded a full reproduction case here: Reproduction Case on GodBolt

Cygon
  • 9,444
  • 8
  • 42
  • 50
  • 1
    `std::size_t actual = Adjuster::template Adjust<0, 0>(value);`. Might also need `typename` after `=` if `Adjust` is a type (not needed if it's a function). – HolyBlackCat Sep 02 '21 at 19:24
  • It's the function you can see in the upper code snippet (`static constexpr TPixel Adjust(TPixel original)`) – Cygon Sep 02 '21 at 19:27
  • Oh. It wasn't included in the godbolt link. – HolyBlackCat Sep 02 '21 at 19:28
  • 2
    @HolyBlackCat Indeed, I wanted to make my repro case as short as possible and the error message came up without it. Now it looks like it *is* relevant for solving the error, though :o) – Cygon Sep 02 '21 at 19:32
  • 2
    @HolyBlackCat I tested my actual code with the `Adjuster::template Adjust<0, 0>(value);` call and this does indeed solve the error. Makes sense that the compile needs to know that I'm calling a template function. If you post your comment as an answer I'll accept it! – Cygon Sep 02 '21 at 19:33
  • 1
    Nah, I'd keep this closed as a dupe. – HolyBlackCat Sep 02 '21 at 19:35
  • Ah, I see the dupe link now. Fine with me. Thanks for your quick help! – Cygon Sep 02 '21 at 19:36

0 Answers0