How to pass a compile-time constant number to a generic lambda

An interest use case for generic lambda’s: they allow passing a compile-time constant number, by using a std::integral_constant as function argument. For example:

auto genericLambda = [](const auto integralConstant) {

  constexpr auto constantValue = decltype(integralConstant)::value;

  float builtInArray[constantValue] = { 0.0f };

  itk::FixedArray<float, constantValue> fixedArray = { { 0.0f } };
};

// To be called, e.g., as follows:
genericLambda(std::integral_constant<unsigned int, 1U>{});
genericLambda(std::integral_constant<unsigned int, 2U>{});
genericLambda(std::integral_constant<unsigned int, 3U>{});

You see, this mitigates the limitation that you cannot explicitly specify the template arguments of a lambda. (C++14 does not support something like genericLambda<42>().)

Unfortunately, this use case appears hampered by a VS2019 Visual C++ compiler bug! It produces a compile error on a similar code example: Compiler Explorer

I just reported the bug to Microsoft: Visual Studio Feedback and their reply was quite interesting! Xiaoxiao Xu [MSFT] replied that the compile error can be avoided by a compiler option!

Something to consider… if more of us are bothered by compiler bugs in the old “lambda processor” of Visual C++, maybe we could add this to our CMakeLists… what do you think?

2 Likes

I don’t mind enabling /Zc:lambda, but we should make sure to only enable it on VS2019 versions (16.8+?) which support it.

1 Like

We must remember that adding such a compile flag will require it for all applications that use ITK since ITK has a large template library component. It can be added to TK_REQUIRED_CXX_FLAGS to do this. So these implications require some consideration.

However it’s not clear to me the if there is a real benefit to this usage in ITK, or just a convince of modern functions ( that are buggy and not widely used apparently ).

p.s. Thanks for the nice post and information on another approach. :upside_down_face:

1 Like

Thanks to you both for your feedback @dzenanz, @blowekamp

A use case for such generic lambda’s that I find interesting is a specific unit test that tests the same requirements for 1D, 2D, and 3D (or ND, in general). For example, the following lambda, checkSizeFill, would test itk::Size<dimensionValue>::Fill for any dimension.

TEST(Size, Fill)
{
  const auto checkSizeFill = [](const auto dimensionConstant) {

    for (const itk::SizeValueType sizeValue : { 0, 1, 2, INT_MAX })
    {
      itk::Size<dimensionConstant> actualSize;
      actualSize.Fill(sizeValue);
      const auto expectedSize = itk::Size<dimensionConstant>::Filled(sizeValue);
      EXPECT_EQ(actualSize, expectedSize);
    }
  };

  // Check itk::Size::Fill for 1D, 2D, and 3D:
  checkSizeFill(std::integral_constant<unsigned int, 1>{});
  checkSizeFill(std::integral_constant<unsigned int, 2>{});
  checkSizeFill(std::integral_constant<unsigned int, 3>{});
}

This checkSizeFill still compiles fine with VS2019. Those buggy compile errors only appear when the generic lambda gets somewhat more complicated!

Note that we can make it still more beautiful by introducing an itk::DimensionConstant alias, to allow writing:

  // Check itk::Size::Fill for 1D, 2D, and 3D:
  checkSizeFill(itk::DimensionConstant<1>{});
  checkSizeFill(itk::DimensionConstant<2>{});
  checkSizeFill(itk::DimensionConstant<3>{});

Do you like it? :smiley:


Edit: Removed decltype(dimensionConstant)::value from the example – it appears unnecessary :smiley:

I agree. It is interesting. Can’t you just use a template function? Or use a google test fixture and make it a member function.

What is the advantage of a lambda here? I was expecting some variadic initializer list, but this seems simpler and with alternatives.

Sure, technically, a checkSizeFill<ImageDimension> could also be written as a template function, or a member function. But in this specific case, it seems preferable to keep checkSizeFill local, within the definition of TEST(Size, Fill). Because it would not be used elsewhere. Just like ordinary local variables are often preferable to globals, or even member variables.

Especially in testing code it might be simpler to introduce a templated helper class. Approaches could vary, but the helper class might have the tested class’s template parameters, and an extra int N parameter if that’s not already one of the parameters. The helper class’s methods would probably not need additional template parameters.

There are anonymous namespaces introduces some time before C++03 that would keep it local to the file, which should be local enough.

BTW @Niels_Dekker I tried to ping you on a PR in SimpleITK with some very interesting variadic templates for typelists that I thought you would find interesting. Here is one class implementation of particular interest:

template <typename... Tls, typename... Trs>
 struct dual_visit<typelist<Tls...>, typelist<Trs...>>
 {
   template <typename Visitor>
   void
   operator()(Visitor && visitor) const
   {
     (void)std::initializer_list<int>{ right_visit<Tls>(visitor)... };
   }

 private:
   template <typename tl, typename tr, typename Predicate>
   static int
   visit_value(Predicate && visitor)
   {
     visitor.CLANG_TEMPLATE operator()<tl, tr>();
     return 0;
   }

   template <typename tl, typename Predicate>
   static int
   right_visit(Predicate && visitor)
   {
     (void)std::initializer_list<int>{ visit_value<tl, Trs>(visitor)... };
     return 0;
   }
 };

:muscle:

@blowekamp Very interesting, your typelist update!

Again, I think generic lambda’s are a useful tool to keep functionality local to their usage. But I’m sorry if I cannot convince either you @blowekamp, @Lee_Newberg

Just for clarification, I encounter situations that look like this:

namespace
{
template <int> void TestAlpha() { /* Test alpha */ }

template <int> void TestBeta() { /* Test beta */ }

template <int> void TestDelta() { /* Test delta */ }

template <int> void TestOmicron() { /* Test omicron */ }
} // namespace

TEST(Covid, Alpha)
{
  TestAlpha<1>();
  TestAlpha<2>();
  TestAlpha<3>();
}

TEST(Covid, Beta)
{
  TestBeta<1>();
  TestBeta<2>();
  TestBeta<3>();
}

TEST(Covid, Delta)
{
  TestDelta<1>();
  TestDelta<2>();
  TestDelta<3>();
}

TEST(Covid, Omicron)
{
  TestOmicron<1>();
  TestOmicron<2>();
  TestOmicron<3>();
}

You see, each of the function templates here is only relevant to the corresponding unit test. Having all those helper function templates “dumped” into the namespace that is shared between all the unit tests in the file seems suboptimal to me. If it doesn’t convince you, at least I hope I made my point clear.

1 Like

For the record, a TEST(Size, Fill) implementation similar to the example code that I posted here (but still slightly improved) is now include with the itk::Size GoogleTest:

From pull request https://github.com/InsightSoftwareConsortium/ITK/pull/3157