2

In my infinite quest to push limits of what can be used as non type template parameter I was trying to see if I can use std::source_location as non type template parameter. That failed with a weird message, since I presume source_location is some magical struct...

type 'std::experimental::source_location' of non-type template parameter is not a structural type

It failed, so I tried to workaround that with using .file_name, but that also fails (godbolt).

note: candidate template ignored: substitution failure: pointer to subobject of string literal is not allowed in a template argument

#include<iostream>
#include<experimental/source_location>

template<auto src_loc = std::experimental::source_location::current().file_name()>
void log_first(){
    static bool dummy =([]{
        std::cout << "Logging first call" + src_loc << std::endl;
    }(), false);
}

int main() {
    log_first();
    log_first();
}

Is there any way to make this work without use of macros?

To be clear I am asking about using source_location as template parameter, not about solving my toy example, it is just here to demonstrate potential use case.

NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
  • 1
    This is invalid argument of template. Note that `const char*` can't be template arguement and this is result of [std::source_location::file_name](https://en.cppreference.com/w/cpp/utility/source_location/file_name). Removing `auto` makes this clear https://godbolt.org/z/GnMa8d – Marek R Dec 08 '20 at 17:14
  • 1
    @MarekR `char const*` _can_ be a template argument, even in C++03 https://godbolt.org/z/MEhPso. It's just that a template parameter cannot point to string literal and the origin of `file_name()` could be a string literal. – Barry Dec 08 '20 at 21:00
  • re. using `source_location::current()` in a default NTTP, see https://stackoverflow.com/questions/64300746/source-locationcurrent-evaluated-as-default-non-type-template-argument – ecatmur Jan 18 '22 at 16:19

1 Answers1

3

std::source_location is specified as:

  struct source_location {
    // ...

  private:
    uint_least32_t line_;               // exposition only
    uint_least32_t column_;             // exposition only
    const char* file_name_;             // exposition only
    const char* function_name_;         // exposition only
  };

And the rules for the kinds of types that can be used as non-template template parameters require that a type be structural, which means, from [temp.param]/7, emphasis mine:

A structural type is one of the following:

  • a scalar type, or
  • an lvalue reference type, or
  • a literal class type with the following properties:
    • all base classes and non-static data members are public and non-mutable and
    • the types of all bases classes and non-static data members are structural types or (possibly multi-dimensional) array thereof.

source_location does not have all of its non-static data members public, so it is not structural, so it is not usable as a non-type template parameter.


This part:

template <auto src_loc = std::experimental::source_location::current().file_name()>

does not work because of [temp.arg.nontype]/3:

For a non-type template-parameter of reference or pointer type, or for each non-static data member of reference or pointer type in a non-type template-parameter of class type or subobject thereof, the reference or pointer value shall not refer to or be the address of (respectively):

  • [...],
  • a string literal object ([lex.string]),
  • ...

But what you can do is create your own type, that is structural, that is constructible from source_location. It's just that the strings can't be char const*, they have to own the data. If you look in the examples in P0732, we can build up:

template <typename Char, size_t N>
struct basic_fixed_string { ... };

template <basic_fixed_string S> struct A {};

using T = A<"hello">;

That might be awkward to deal with in this case, so you could also just pick some reasonable max size and go with that.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • do you know why constexpr const char* file_name() const noexcept; is also not allowed? Maybe because parent object is not constexpr friendly or member I get pointer to is not consexpr friendly or... – NoSenseEtAl Dec 08 '20 at 17:14
  • OP is trying to use `.file_name()` as the object, not a `source_location` directly, so the part you emphasized in the quote is not really the right one. – Holt Dec 08 '20 at 17:16
  • 1
    @NoSenseEtAl Right, well the quote applies to both, it's just the emphasized part. – Holt Dec 08 '20 at 17:17
  • Barry I did not really got your last part of the answer, did you mean something like this? https://godbolt.org/z/6od6EP Problem with this is that it seems that source_location::current is broken for this usecase, since I get location of template function, not callers(I presume because "calling" of current is done at compile time). Do you know if this is by design or just a unfortunate limitation? – NoSenseEtAl Dec 09 '20 at 04:44