These are chat archives for boostorg/hana

14th
Mar 2016
Barrett Adair
@badair
Mar 14 2016 14:51
With regard to Apendix I: Advanced constexpr, what are the problems with this potential non-Hana solution (macro usage notwithstanding)?
#define CONSTEXPR_ARG(name, ...) \
struct { constexpr decltype(auto) operator()(){ return __VA_ARGS__; } } name

template<typename T>
constexpr decltype(auto) value_of(T) {
    return T{}();
};

//user code begins here

template <typename T>
void f(T t) {
    static_assert(value_of(t) == 1, "");
}

int main() {
    CONSTEXPR_ARG(one, 1);
    f(one);
}
Jason Rice
@ricejasonf
Mar 14 2016 14:55
Older versions of clang won't let you male a copy of 't'.
*make
Barrett Adair
@badair
Mar 14 2016 14:57
Because constructors weren't default constexpr back then? That was a standard compliance issue, right? (or C++11 vs. C++14 anyway)
Jason Rice
@ricejasonf
Mar 14 2016 15:00
Either that or they weren't checking to see if the copy constructor actually did anything.
Barrett Adair
@badair
Mar 14 2016 15:02
Any issues besides that? I've never played with local classes before, and was surprised that I could "capture" other locals inside the operator() definition - in a cursory experiment, anyway. I was wondering if you or Louis had been down this road before. Just bouncing ideas around - not searching for a non-Hana solution :)
By the way, I think "appendix" has two 'P's
I'm not 100% certain on that, but that's what sketchy dictionary websites in Google's search results want me to believe
Jason Rice
@ricejasonf
Mar 14 2016 16:13
I was unaware of the local class thing.
at least inside functions
Louis Dionne
@ldionne
Mar 14 2016 18:46
@badair Thanks for the correction on appendix.
@badair The issue with the above code is that you are reading from a non-constexpr variable in value_of. Here’s how it goes: inside f, t is not constexpr. But value_of takes its argument by value, which means that you must call its copy constructor, which means that you must read from it. So even if you make t’s copy constructor constexpr, it won’t cut it because you’ll be calling the constructor (which is constexpr) on t, which is not constexpr.
But if you change to value_of(T const&), then you won’t be reading from your argument and it’s going to work.
Louis Dionne
@ldionne
Mar 14 2016 18:52
Ah, but Clang 3.7 can tell that t’s copy constructor does not read from anything, and hence you’re not really reading from a non-constexpr variable and it works.
Anyway, I just always use T const&, to be sure. @badair, what do you mean by capturing locals inside operator()?
Barrett Adair
@badair
Mar 14 2016 20:11
@ldionne It's either a GCC compiler bug or a crazy feature I don't fully understand, but the following code compiles in Wandbox's GCC 6:
I can't for the life of my figure out this chat box
#define CONSTEXPR_ARG(name, ...) \
struct { constexpr decltype(auto) operator()(){ return __VA_ARGS__; } } name

template<typename T>
constexpr decltype(auto) value_of(T) {
    return T{}();
};

//user code begins here

template <typename T>
void f(T t) {
    static_assert(value_of(t) == 3, "");
}

int main() {

    constexpr int i = 1;
    constexpr int j = 2;

    CONSTEXPR_ARG(one, i + j);
    f(one);
}
This expands to:
template<typename T>
constexpr decltype(auto) value_of(T) {
    return T{}();
};

//user code begins here

template <typename T>
void f(T t) {
    static_assert(value_of(t) == 3, "");
}

int main() {

    constexpr int i = 1;
    constexpr int j = 2;

    struct {
        constexpr decltype(auto) operator()(){ 
            return i + j; 
        }
    } one;

    f(one);
}
I don't know what the hell to make of this, because it seems too crazy to be true.
Barrett Adair
@badair
Mar 14 2016 20:16
i and j being used in operator()., that is.
If this is not a compiler bug, then I'd imagine the copying of this anonymous struct is irrelevant to the result, which means const T& would work fine as you say.
The name one doesn't make any sense at all in this example, but you get the point.
Jason Rice
@ricejasonf
Mar 14 2016 20:43
@badair I know you didn't address me, but why wouldn't that work?
oh.. the struct using i and j?
Barrett Adair
@badair
Mar 14 2016 20:47
This is even more terrifying:
#define PP_CAT_(X, Y) X ## Y
#define PP_CAT(X, Y)  PP_CAT_(X, Y)

#define CONSTEXPR_ARG(name, ...) \
struct PP_CAT(name, _12345_unique) { constexpr decltype(auto) operator()(){ return __VA_ARGS__; } }; \
constexpr auto name = PP_CAT(name, _12345_unique){}

template<typename T>
constexpr decltype(auto) value_of(T) {
    return T{}();
};

struct foo {
    template<typename T>
    constexpr auto operator()(T t) {
        return value_of(t);
    }
};

template <typename T>
void f(T t) {
    static_assert(value_of(t) == 1, "");
}

int main() {

    CONSTEXPR_ARG(a, 1);
    CONSTEXPR_ARG(b, foo{});
    CONSTEXPR_ARG(c, value_of(b)(a));

    f(c);
}
and yes that's right.
It appears that GCC may be implementing constexpr locals as static storage duration internally, which is why you can reference them from a local class member function.
Jesus - this works in clang
#define PP_CAT_(X, Y) X ## Y
#define PP_CAT(X, Y)  PP_CAT_(X, Y)

#define CONSTEXPR_ARG(name, ...) \
struct PP_CAT(name, _12345_unique) { constexpr decltype(auto) operator()(){ return __VA_ARGS__; } }; \
static constexpr auto name = PP_CAT(name, _12345_unique){}

template<typename T>
constexpr decltype(auto) value_of(T) {
    return T{}();
};

struct foo {
    template<typename T>
    constexpr auto operator()(T t) {
        return value_of(t);
    }
};

template <typename T>
void f(T t) {
    static_assert(value_of(t) == 1, "");
}

int main() {

    CONSTEXPR_ARG(a, 1);
    CONSTEXPR_ARG(b, foo{});
    CONSTEXPR_ARG(c, value_of(b)(a));

    f(c);
}
__LINE__ is probably better than ..._12345_unique, but whatever. @ldionne would you like me to move this conversation elsewhere?
Barrett Adair
@badair
Mar 14 2016 20:54
@ricejasonf I have no idea what the standard says about this yet, but Clang and GCC appear to be in consensus that you can reference static locals in nested class member functions, and that the constexpr-ness carries over. If the above program is well formed, the overly-enthusiastic half of me would like to think this opens up a world of possibilities.
Jason Rice
@ricejasonf
Mar 14 2016 20:55
What do you mean by constexpr-ness carries over?
Barrett Adair
@badair
Mar 14 2016 20:55
I'll PP-expand the above program and explain. One moment
P.S. If this is a new technique, can I have dibs on the first stack overflow question? :)
Barrett Adair
@badair
Mar 14 2016 21:01
@ricejasonf Does this make sense?
template<typename T>
constexpr decltype(auto) value_of(T) {
    return T{}();
};

struct foo {
    template<typename T>
    constexpr auto operator()(T t) {
        return value_of(t);
    }
};

template <typename T>
void f(T t) {
    static_assert(value_of(t) == 1, "");
}

int main() {

    struct a_12345_unique {
        constexpr decltype(auto) operator()() { return 1; }
    };

    //a is a static, local, constexpr variable
    static constexpr auto a = a_12345_unique{};


    struct b_12345_unique {
        constexpr decltype(auto) operator()() { return foo{}; }
    };

    //b is a static, local, constexpr variable
    static constexpr auto b = b_12345_unique{};

    struct c_12345_unique {
        //Whoa! We can use these locals inside of this local struct!
        //What's more, they are still constexpr!
        constexpr decltype(auto) operator()() { return value_of(b)(a); }
    };

    //c is a static, local, constexpr variable
    static constexpr auto c = c_12345_unique{};

    //f uses the value_of trick to get the constexpr expression back out of operator()
    f(c);
}
We are basically freeze-drying constexpr expressions inside of the operator() of some local type, and bypassing the entire argument/parameter system
Barrett Adair
@badair
Mar 14 2016 21:06
normally, constexpr arguments are "decayed" to non-constexpr parameters. With this technique, we don't pass constexpr arguments - we define a local struct and "steal" expressions from the current scope.
I didn't sleep last night so tell me if I sound crazy
Jason Rice
@ricejasonf
Mar 14 2016 21:07
You never actually touch the argument. You default construct a new value from it's type.
Barrett Adair
@badair
Mar 14 2016 21:08
Exactly - the argument is just a dumb vessel for the expressions, which are baked into the operator()
In other words, c_12345_unique is copied, but we don't really care. You can achieve the same result without copies
From a user perspective, it works like this:
Declare an expression to be a constexpr argument with the CONSTEXPR_ARG macro. Functions accepting CONSTEXPR_ARGs must unwrap them with value_of
I think I want to write a blog post about it
Jason Rice
@ricejasonf
Mar 14 2016 21:12
CONSTEXPR_ARG is doing exactly what hana::type_c and hana::int_c do.
although hana::type_c doesn't have a callable operator
Barrett Adair
@badair
Mar 14 2016 21:15
Maybe I misunderstand things, but int_c is limited to the very few things you can pass to a template by-value. CONSTEXPR_ARG has no such limitations (assuming this is well-formed). My understanding of hana::type_c is that it's a variable template of ahana::typeobject, which is not very similar toCONSTEXPR_ARG`, to me.
I'm just butchering this whole markdown thing today
Jason Rice
@ricejasonf
Mar 14 2016 21:17
lol
They store a constexpr value within a type.
Barrett Adair
@badair
Mar 14 2016 21:18
Well, maybe I misunderstand things completely. Haha
Jason Rice
@ricejasonf
Mar 14 2016 21:23
#if 0
    struct a_12345_unique {
        constexpr decltype(auto) operator()() { return 1; }
    };


    //a is a static, local, constexpr variable
    static constexpr auto a = a_12345_unique{};
#endif
    static constexpr auto a = hana::int_c<1>;
Here hana::int_c<1> is a drop in replacement.
Barrett Adair
@badair
Mar 14 2016 21:25
So is std::integral_constant, for that particular line of code, yes? The limitation here is that you can't say std::integral_constant<foo, foo{}>
Jason Rice
@ricejasonf
Mar 14 2016 21:25
because foo is a type
Barrett Adair
@badair
Mar 14 2016 21:26
Yes
I'm going to ease off until Louis weighs in, because I don't want to pollute his gitter while he's AFK. Feel free to continue discussing this on mine
Jason Rice
@ricejasonf
Mar 14 2016 21:27
he didn't seem to mind the benchmark stuff with D discussion :P
Louis Dionne
@ldionne
Mar 14 2016 22:49

Sorry, I was/am busy working on Metabench. That shit is time consuming, but I hope it’ll be useful. Ok, back to business. First, I must admit that I am very surprised that the following works on GCC (edit: but not Clang):

int main() {
    constexpr int i = 333;
    struct local {
        constexpr int operator()() const { return i; }
    };

    static_assert(local{}() == 333, "");
}

I wouldn't expect the local variable i to be "captured" (à la lambda capture) in the local struct, but I think it is indeed valid because it is not ODR-used. So this would be a Clang bug, but don't quote me on that. On the other hand, I am not really surprised that the following code works:

int main() {
    static constexpr int i = 333; // <- notice static
    struct local {
        constexpr int operator()() const { return i; }
    };

    static_assert(local{}() == 333, "");
}

It works on both Clang and GCC, as expected. The relevant part of the standard is [class.local] (section 9.8 of the C++14 standard).

But the first snippet I posted above is ill-formed if i is ODR-used, which I honestly only have a vague idea what it means.
For the record, Hana uses something similar to your macro to implement compile-time strings: https://github.com/boostorg/hana/blob/master/include/boost/hana/string.hpp#L92
I thought about generalizing the macro to BOOST_HANA_CONSTANT or something like that, but I thought it was not useful. Perhaps I was wrong.
Barrett Adair
@badair
Mar 14 2016 22:54
Oh, nice! Yes, I remember discussing the symbol table optimization and then never following up on it. Haha.
Louis Dionne
@ldionne
Mar 14 2016 22:54
I think you want to post that on CLBL’s gitter?
Barrett Adair
@badair
Mar 14 2016 22:55
No, I remember discussing the symbol length optimization for anonymous types in one of Hana's issue threads
Anyway, I'm relieved to hear that you've already been down this road (once again).
Jason Rice
@ricejasonf
Mar 14 2016 22:57
@ldionne why is this exploding when the param is const&?
#include<boost/hana.hpp>

namespace hana = boost::hana;

template <typename T>
void check_1(T t) {
  static_assert(hana::value(t) == 17, "");
}

template <typename T>
void check_2(T const& t) {
  // explodes
  static_assert(hana::value(t) == 17, "");
}

int main() {
  check_1(hana::int_c<17>);
  check_2(hana::int_c<17>);
}
Louis Dionne
@ldionne
Mar 14 2016 22:58

No, I remember discussing the symbol length optimization for anonymous types in one of Hana's issue threads

Ah! Ok sorry it didn’t click.

Jason Rice
@ricejasonf
Mar 14 2016 22:59
btw that is from our extended discussion on clbl's chat
Louis Dionne
@ldionne
Mar 14 2016 23:00

why is this exploding when the param is const&?

Not sure, but it could be because the reference itself, which is not a constant expression, is being read? If you replace T const& by T const*, which is the same for the compiler, does it make more sense?

Barrett Adair
@badair
Mar 14 2016 23:05
I think a BOOST_HANA_CONSTANT macro would be interesting. You could wrap it in a hana::type and everything would be hunky dory. But I don't know what the advantages would be off the top of my head, other than "cool constexpr hack".
Louis Dionne
@ldionne
Mar 14 2016 23:06
Until we get constexpr lambdas, and until they can appear in unevaluated contexts, such a macro would be quite limited.
What this really is is a library-based implementation of constexpr arguments.
i.e.
void f(constexpr int i) { static_assert(i == 3, “”); }
Barrett Adair
@badair
Mar 14 2016 23:07
That's what I gathered from your cppcon talk
Louis Dionne
@ldionne
Mar 14 2016 23:07
Yes, it’s exactly this.
A friend of mine was working on hana::Constantifying functions at some point.
Barrett Adair
@badair
Mar 14 2016 23:08
And that's why I came here first with my not-so-genius idea.
Louis Dionne
@ldionne
Mar 14 2016 23:09
Basically the idea was that you might be able to life a constexpr function into some kind of wrapper, and then you lift constexpr arguments into such a wrapper too. You can pass the wrapper around and even if the wrapper is not constexpr, you can retrieve the constexpr content inside it.
Then, IIRC, you realize that the whole construct is an applicative (lift is lifting a constexpr value to the wrapper and ap is applying a wrapped function to wrapped arguments).
lol. This is a deep hole for those that are curious enough.
Barrett Adair
@badair
Mar 14 2016 23:13
I'm not a Haskellite, unfortunately. The hole is a bit deeper for me. My entire days now are spent in deep holes, so FP is on my list. My only real exposure to FP is template metaprogramming and eliminating state in my own C# programs at my old job. I get the big picture, but much of the terminology escapes me.
I really like your explanation of the quadrants of execution.
Louis Dionne
@ldionne
Mar 14 2016 23:14
Oh, no problem regarding Haskell. Actually, Haskellers will tell you this stuff is category theory. :p

I really like your explanation of the quadrants of execution.

Well thanks!

Barrett Adair
@badair
Mar 14 2016 23:15
Category theory
scribbles on notepad
Louis Dionne
@ldionne
Mar 14 2016 23:15
I was trying to classify the different realms of computations that we could access through C++, and came up with that.
Honestly, unless you have a formal education in mathematics, you might find category theory a bit dry. If you want to touch these concepts, I would recommend something like the typeclassopedia or this.
But if you read one of the “classics”, you’ll die after 10 pages at most.
Barrett Adair
@badair
Mar 14 2016 23:17
Bookmarked, thanks a lot! I'll probably start tomorrow.
Louis Dionne
@ldionne
Mar 14 2016 23:18
The texts are for graduate students with already some good experience in abstract algebra and other math topics like that.
I mean, not the texts above. But the classics.
Barrett Adair
@badair
Mar 14 2016 23:18
I've thought about going back to undergrad for math. I only finished Calc 1. I enjoyed it
Are you headed to grad school?
Louis Dionne
@ldionne
Mar 14 2016 23:19
Nope, I finished my undergrad in December and I’ll be working in the US in may.
Barrett Adair
@badair
Mar 14 2016 23:20
I definitely want to go to graduate school, but I also really value financial independence.
Very cool.
Louis Dionne
@ldionne
Mar 14 2016 23:20
I would like to eventually go to graduate school too, but perhaps after I have some real world experience. And the financial independence is also nnnnnnice.
I meant just “nice”, but my keyboard is retarded.
Barrett Adair
@badair
Mar 14 2016 23:20
hahaha.
Louis Dionne
@ldionne
Mar 14 2016 23:21
Now I look like a greedy person. lol
Anyway, I’m going back to Metabench.
Jason Rice
@ricejasonf
Mar 14 2016 23:21
gggrrrrreedy
Barrett Adair
@badair
Mar 14 2016 23:21
Haha. I'm gonna go eat. Later
Looking forward to Metabench
Louis Dionne
@ldionne
Mar 14 2016 23:21
lol. cya
Yeah, I hope it’ll work as well as I want.
Bruno and I are working pretty hard on it, and I think it’s going to work well. The idea is that you should be able to drop a single CMake module in your tree and then start writing benchmarks. With the minimum of overhead.
As little configuration and hassle as possible, etc.
I need this constantly for talks and blog posts, and it’s always a pain to setup a small benchmarking framework. Hopefully we’ll be done once and for all.