I have a namespace Bar
and a namespace Bar::Inner
(declared within Bar
).
// Bar
namespace Bar {
size_t num = 32;
// Bar::Inner
namespace Inner {
size_t num = 4;
};
};
Then, I have a variadic function template foo
. This variadic template accepts non-type parameters of common type T
.
However, I want the non-type parameters to HAVE TO be members of namespace Bar
, but NOT members of namespace Bar::Inner
.
template<typename T, T ...Sz>
requires MyConcept<T, Sz...>
auto foo()
{
return (Sz + ...);
}
And I'd like to accomplish that using concepts, if possible.
template<typename T, T ...Sz>
concept MyConcept = (/*Whatever goes here*/);
So, to sum it up, I basically need a concept that can allow the non-type template parameters only if they are members of namespace Bar
, but NOT of namespace Bar::Inner
.
It's entirely possible to filter variables by namespace if you allow yourself to use some extensions.
You have to also know that you won't accept values anymore. This may be a big downside.
First, let's declare fixed_string
, this is gonna be useful for compile time string manipulation:
template<std::size_t n>
struct fixed_string {
constexpr fixed_string() = default;
constexpr fixed_string(const char(&str)[n + 1]) noexcept {
auto i = std::size_t{0};
for (char const c : str) {
_data[i++] = c;
}
}
friend constexpr auto operator<=>(fixed_string const&, fixed_string const&) = default;
[[nodiscard]]
static constexpr auto size() noexcept -> std::size_t {
return n;
}
constexpr auto data() const& noexcept {
return _data;
}
constexpr auto data() & noexcept {
return _data;
}
constexpr auto begin() const& noexcept -> char const* {
return _data;
}
constexpr auto end() const& noexcept -> char const* {
return _data + n;
}
constexpr auto begin() & noexcept -> char* {
return _data;
}
constexpr auto end() & noexcept -> char* {
return _data + n;
}
constexpr auto operator[](std::size_t index) noexcept {
return _data[index];
}
constexpr auto starts_with(std::string_view str) {
return std::string_view{_data, n}.starts_with(str);
}
char _data[n + 1];
};
template<std::size_t n>
fixed_string(char const(&)[n]) -> fixed_string<n - 1>;
Now that we have the basic, let's create a function that returns the qualified name of an object using a compiler extension. It's technically doable pretty portably if you use compiler specific extension and you dynamically filter the string so it returns only the name of the object.
template<auto& v>
constexpr auto name_of() -> std::string_view {
constexpr auto size = std::string_view{__PRETTY_FUNCTION__}.size();
constexpr auto drop_begin = "constexpr std::string_view name_of() [with auto& v = "sv.size();
constexpr auto drop_end = "; std::string_view = std::basic_string_view<char>]"sv.size();
return std::string_view{__PRETTY_FUNCTION__}.substr(drop_begin, size - drop_end - drop_begin);
}
Then, we can create a concept to only yield true of the qualified name starts with a particular string:
template<auto& v, fixed_string namesp>
concept in_namespace = name_of<v>().starts_with(std::string_view{namesp.data(), namesp.size()});
Now, you can write a function that only accepts objects from a particular namespace:
template<auto& v> requires in_namespace<v, "bar">
void im_clever();
To filter out the inner namespace you can do this:
template<auto& v>
concept in_bar_not_in_inner = in_namespace<v, "bar"> and not in_namespace<v, "bar::inner">;