Hello everyone!Lately I've been concerned with how we access tuple elements.The syntax 'std::get<0>(t)' seems highly unintuitive to me.What I would really like to be able to write is 't[0]'.For this we need constexpr function parameters. So we can define operator[] as follows:auto operator[] (constexpr int i) -> decltype(std::get<i>(*this)) {return std::get<i>(*this);}(or something similar)
I think this would help not only for tuples.For example, it is not possible today to pass arbitrary constexpr objects to functions because you must pass them as a template parameter.
You are missing the point.This has nothing to do with constexpr functions.What I'm proposing is to be able to pass constexpr objects with no regard as to the function being constexpr.In other words but I'm proposing is for the following syntax:void foo(constexpr int i) {}to be indentical to:template <int i>void foo() {}The difference is that the constexpr version is easier to understand and that it allows youto pass arbitrary constexpr object types instead of just some types.
Furthermore, there's no reason why a function's arguments should ever be prevented from taking values that aren't compile-time constants. What would you use it for? Why would you want to prevent someone from calling `foo(integerValue)`?
Lately I've been concerned with how we access tuple elements.The syntax 'std::get<0>(t)' seems highly unintuitive to me.What I would really like to be able to write is 't[0]'.
#include <iostream>#include <tuple>#include <type_traits>#include <string>
template <size_t i>using Index = std::integral_constant<size_t, i>;
template <class... Types>struct Tuple : std::tuple<Types...>{ using std::tuple<Types...>::tuple; template <size_t i> auto operator [] (const Index<i>&) -> decltype(std::get<i>(*this))
{ return std::get<i>(*this); }
template <size_t i> auto operator [] (const Index<i>&) const -> decltype(std::get<i>(*this))
{ return std::get<i>(*this); }
};
template <size_t N, char... Digits>struct Number_ : std::integral_constant<size_t, N> {};
template <size_t N, char Digit, char... Digits>struct Number_<N, Digit, Digits...> : std::integral_constant<size_t, Number_<N * 10 + (Digit - '0'), Digits...>::value>{ static_assert(('0' <= Digit) && (Digit <= '9'), "Invalid digit in index string");};
template <char... Digits>struct Number : Number_<0, Digits...> {};
template <char... Digits>constexpr Index<Number<Digits...>::value> operator "" _i() noexcept{ return {};}
int main() { int i = 42; double f = 3.14; std::string s = "Hello, world!"; Tuple<int, double, std::string> t(i, f, s); std::cout << t[0_i] << std::endl; std::cout << t[1_i] << std::endl; std::cout << t[2_i] << std::endl;}
// ...
template <size_t i>constexpr Index<i> I{};
// ...
std::cout << t[I<0>] << std::endl;std::cout << t[I<1>] << std::endl;std::cout << t[I<2>] << std::endl;
// ...
Lately I've been concerned with how we access tuple elements.The syntax 'std::get<0>(t)' seems highly unintuitive to me.What I would really like to be able to write is 't[0]'.It can be achieved using C++11 user-defined literals:
(Although, I can still imagine that it's somehow non-standard, but it's just formality after major compilers accept it :) )
#include <tuple>
#include <string>
template<typename... Types>
struct Purr {
std::tuple<Types...> t;
template<int i>
auto get() -> decltype(std::get<i>(t)) { return std::get<i>(t); }
};
int main() {
Purr<int, double, std::string> p;
p.get<0>() = 1;
p.get<1>() = 4.5;
p.get<2>() = "meow";
}
Works in MSVC2013, GCC 4.9.0 and Clang 3.3: http://goo.gl/MPHhMb
int foo(constexpr bool opt1, constexpr bool opt2) {
if (opt1) lengtyStuff();
moreStuff();
if (opt2) stuffstuff();
evenMoreStuff();
}
int main() {
bool userChoice1 = askUser();
bool
userChoice2 = askUser();
foo(userChoice
1, userChoice2
); //generates code with 2 branch instructions
foo(true, false); //no branch instruction, aka "foo<true, false>"
foo(userChoice1, true); //1 branch instr, aka "foo<true>(userChoice1)"
}
Sorry for top-posting. I want to bring this issue again, please let's
make some effort to find a solution since it's important.
This would be very useful allowing lambdas in TMP, and also for static_asserts.
There is also a specific but yet real-life issue, which is intrinsics:
some intrinsics require arguments to be literals since those arguments
will become immediates in the associated instruction.
I'm trying to write an AES related program using Intel's AES-NI for
which gcc (and I think MSVC) have intrinsics, such as
_mm_aeskeygenassist_si128 ("the last argument must be an 8-bit
immediate").
There is no C++ way of doing this program with compile time loop
unrolling. If I would be able to do
Loop<0, 9>::operate([ ](constexpr unsigned int index) {
the_intrinsic(index); });
with the constexpr argument transformed into a non-type template
argument, then the problem would be solved.
I could also say static_assert(index < 10) and it would work too.
A simple example is strlen
template<typename... Ts>
class tuple
{
/* ... */
const auto& operator[]( constexpr std::size_t index ) const {
return std::get<index>(*this);
}
};
//compile-time only function
//it will never have linkage
//all its arguments are constexpr
//a non-constexpr expression usage in its definition will cause an error instead of a fallback like normal constexpr
//is only called in an explicit constexpr context unlike normal constexpr
constexpr(true) auto func(Literal r) {}
//runtime specialization of func
/*constexpr(false)*/ auto func(Literal r) {}
//error redefinition of 'constexpr(false) void func(Literal)'
constexpr void func(Literal) {}
constexpr(true) auto func2();
//allowed
//constexpr is ignored, might warn, retains inline side effects
constexpr auto func2();
constexpr auto func3();
constexpr auto error_0 = func3();
//error: constexpr specialization of 'func3' after instanciation
constexpr(true) auto func3();
//addresses demands for partially constexpr argument-list
constexpr(true) void partial_constexpr(int constant)
{
return [](auto&&... runtime_args) /*normal constexpr lambda*/ {
//runtime code
//can use 'constant' as a constexpr here without capture
};
}
int main()
{
constexpr auto r_0 = func(Literal{}); //constexpr(true) version called
auto r_1 = func(Literal{}); //constexpr(false) version called
partial_constexpr(42/*compile-time args*/)(/*runtime args*/);
auto r_2 = constexpr func(Literal{}); //also proposed unary constexpr operator for explicit selection, error if the expression is not constexpr
//is equivalent to
auto r_3 = []() { constexpr auto __ret__ = func(Literal{}); return __ret__; }();
}
//replacing of gnu's operator"" extension
constexpr(true) auto operator""_string_literal(const char *str, size_t len)
{
return []<size_t... Indexs>(std::index_sequence<Indexs...>&&)
{
return TemplateCharSeq<str[Indexs]...>{};
}(std::make_index_sequence<len>{});
}
//operator[] for std::tuple
template <class... Args>
struct tuple
{
...
constexpr(true) auto& operator[](size_t i) const
{
static_assert(i < sizeof...(Args));
return std::get<i>(*this);
}
constexpr(true) auto& operator[](size_t i)
{
static_assert(i < sizeof...(Args));
return std::get<i>(*this);
}
....
};
template <class... Args> struct Select : Args... { using Args::operator()...; };
template <class... Args> Select(Args&&...) -> Select<Args...>;
//for lambdas
void func_3()
{
constexpr auto f_0 = []() {}; //operator() is constexpr /*fallback on runtime*/, no changes
constexpr auto f_1 = []() constexpr(false) {}; //operator() cannot be called at compile-time
constexpr auto f_2 = []() constexpr(true) {}; //operator() can only be called a compile-time and explicitly
constexpr Select f_3 = { f_1, f_2 };
constexpr auto r_4 = f_3(); //f_2
auto r_5 = f_3(); //f_1
}
auto t = constexpr expr; //will error if expr cannot resolve into a constexpr expression
I made an analogue proposal a while back but except for one positive opinion it didn't garner much attention.My proposal is a counter proposal of sort: constexpr(true) specialization.
- A constexpr(true) being a constexpr function/method that can't fallback into a runtime function.
- can only call constexpr method & functions.
- must be requested explicitly (constexpr assign, constexpr unary operator).
- cannot issue linkage (no mangling for itself nor for the types that it uses internally), thereby solving the issue of literal non-type template parameters
- all its arguments are constexpr and usable as such.
- A runtime and a compile-time version of a same function for more efficient runtime and more powerful compile-time coding
Not a bad idea...However, it does not allow someone to partially make a function constexpr.If only the first parameter of a function needs to be constexpr, your methodology forces them all to be constexpr. This is unneccessarily restricted to my impression.
constexpr(true) auto f(int const_expr)
{
return [](auto&&... runtime_args) //normal constexpr lambda, might produce linkage within the caller's context
{
//const_expr usable as a constexpr here
//whilst runtime_args are not
...
};
}
int main()
{
int i = 0;
f(42/*constexpr args here*/)(i/*runtime args here*/);
}
And furthermore I feel, the fact that function parameters have to be constexpr in order to invoke the funciton is more obviously conveyed if the parameters are being annotated with constexpr.Because that's what is important for the user, not that the compiler doesn't need to generate assembly for it. Does that make sense?This keeps the thing intuitive and has the same (or even greater opportunities as your solution since we then are able to overload on individual parameters depending on their constexpr-ness).That is, given compilers are allowed to deduce the neccessity of linkage depending on whether all parameters are annotated 'constexpr'.I still prever the annotation of parameters with 'constexpr' :|Jakob
template<std::string_view I> void func(int runtime);
void func(constexpr std::string_view i, int runtime);