Binary size when using reflect::for_each
SpintroniK opened this issue · 11 comments
This isn't really an issue, but I was wondering if it would be possible to improve the for_each function.
I've noticed that this example: https://godbolt.org/z/sqvn9bh1j shows how to overload the operator<< to print a struct's variables names and values using reflect::for_each.
Looking at the output from gcc, I see this : .string "constexpr std::string_view reflect::v1_0_8::detail::function_name() [with auto V = ref<const int>{ext<foo>.foo::a}; std::string_view = std::basic_string_view<char>]"
twice, because the struct has two members.
My guess is that this library kind of started from there : https://www.reddit.com/r/cpp/comments/18bv197/c20_60_loc_constexpr_get_name_for_members_no.
My question is: can we avoid calling get_name<0>(...)
, get_name<1>(...)
and pass all the struct's members directly as non-type template parameters in order to get a single line in the final binary?
I don't know if that makes sense, so I tried to make sure this is actually possible. This is (a very bad example of) what I came up with: https://godbolt.org/z/hxeKno1Pv.
Would it be an improvement to add that sort of thing in your implementation?
Would that actually reduce the overall binary size (thinking about embedded systems here)?
Thanks for pointing it out, defo an issue and thanks for your suggestions, it's important to fix that. Will take a look at keep updates in this thread.
Yeah, the implementation is broken ATM, the whole array can be precomputed at compile time with lazy fields. Going to fix that. Thanks again for pointing it out.
No worries. Looking forward to seeing how it works under the hood!
Have been playing around and managed to remove the bloat and optimize the code - right now for enum_name but the same applies to others. Full example here - https://t.co/6RvagrWa00
Looks awesome! Thanks for sharing your progress. I'm guessing you won't be using mph in the final implementation?
Sorry, gone a bit too wide with the example. I don't think name_to_enum belongs in reflection either way but likely reflect will end up with some policies to trade-off run-time performance/binary size or compilation-times as different solutions have different properities.
No worries. Having some policies is a pretty good idea. It's one of the features I like the most with sml.
type_name and member_name have been addressed by eeb8606, enum_name is still WIP
With -Os https://godbolt.org/z/3r6WsGbh7
That is pretty good indeed!
Since you mentioned the potential addition of policies, do you think it would be possible to "customize" the member
struct?
For instance, if I only want the name and value of my aggregate's members, would that be possible to have a policy that allows to keep only those fields in the member
struct?
for_each as well as enum_name now have been improved with minimal size and optimized performance.
for_each API has been changed to allow getting only require fields
Full example here - > https://godbolt.org/z/fcdGTT7ff
For the following examples
#include <iostream>
int main() {
reflect::for_each([](auto I) {
std::cout
<< reflect::type_name(f) << '.' // foo, foo
<< reflect::member_name<I>(f) << ':' // a , b
<< reflect::type_name(reflect::get<I>(f)) << '=' // int, E
<< reflect::get<I>(f) << '(' // 42 , B
<< reflect::size_of<I>(f) // 4 , 4
<< reflect::align_of<I>(f) // 4 , 4
<< reflect::offset_of<I>(f) << ')' // 0 , 4
<< '\n';
}, f);
}
It produces (with -Os)
main:
push rbx
mov edx, 3
mov esi, OFFSET FLAT:reflect::v1_0_9::type_name<foo>()::{lambda()#1}::operator()() const::name
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov esi, 46
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)
mov edx, 1
mov esi, OFFSET FLAT:reflect::v1_0_9::member_name<0ul, foo>(foo const&)::{lambda()#1}::operator()() const::name
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov esi, 58
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)
mov edx, 3
mov esi, OFFSET FLAT:reflect::v1_0_9::type_name<int>()::{lambda()#1}::operator()() const::name
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov esi, 61
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)
mov esi, 42
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov esi, 40
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)
mov esi, 4
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<unsigned long>(unsigned long)
mov esi, 4
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<unsigned long>(unsigned long)
xor esi, esi
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<unsigned long>(unsigned long)
mov esi, 41
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)
mov esi, 10
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)
mov edx, 3
mov esi, OFFSET FLAT:reflect::v1_0_9::type_name<foo>()::{lambda()#1}::operator()() const::name
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov esi, 46
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)
mov edx, 1
mov esi, OFFSET FLAT:reflect::v1_0_9::member_name<1ul, foo>(foo const&)::{lambda()#1}::operator()() const::name
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov esi, 58
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)
mov edx, 1
mov esi, OFFSET FLAT:reflect::v1_0_9::type_name<E>()::{lambda()#1}::operator()() const::name
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov esi, 61
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)
mov edi, 1
mov rbx, rax
call std::basic_string_view<char, std::char_traits<char> > reflect::v1_0_9::enum_name<E, reflect::v1_0_9::fixed_string<char, 0ul>{}, -1, 128>(E)
mov rdi, rbx
mov rsi, rdx
mov rdx, rax
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov esi, 40
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)
mov esi, 4
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<unsigned long>(unsigned long)
mov esi, 4
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<unsigned long>(unsigned long)
mov esi, 4
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<unsigned long>(unsigned long)
mov esi, 41
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)
mov esi, 10
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)
xor eax, eax
pop rbx
ret
reflect::v1_0_9::member_name<1ul, foo>(foo const&)::{lambda()#1}::operator()() const::name:
.byte 98
reflect::v1_0_9::member_name<0ul, foo>(foo const&)::{lambda()#1}::operator()() const::name:
.byte 97
reflect::v1_0_9::enum_name<E, reflect::v1_0_9::fixed_string<char, 0ul>{}, -1, 128>(E)::{lambda<auto $N0>()#1}::operator()<1u>() const::{lambda()#1}::operator()() const::name2:
.byte 66
reflect::v1_0_9::enum_name<E, reflect::v1_0_9::fixed_string<char, 0ul>{}, -1, 128>(E)::{lambda<auto $N0>()#1}::operator()<0u>() const::{lambda()#1}::operator()() const::name2:
.byte 65
reflect::v1_0_9::enum_name<E, reflect::v1_0_9::fixed_string<char, 0ul>{}, -1, 128>(E)::{lambda<auto $N0>()#1}::operator()<4294967295u>() const::{lambda()#1}::operator()() const::name2:
.byte 40
.byte 69
.byte 41
.byte 52
.byte 50
.byte 57
.byte 52
.byte 57
.byte 54
.byte 55
.byte 50
.byte 57
.byte 53
reflect::v1_0_9::type_name<E>()::{lambda()#1}::operator()() const::name:
.byte 69
reflect::v1_0_9::type_name<foo>()::{lambda()#1}::operator()() const::name:
.byte 102
.byte 111
.byte 111
reflect::v1_0_9::type_name<int>()::{lambda()#1}::operator()() const::name:
.byte 105
.byte 110
.byte 116
Closing for now as fixes will be released in the next version. Feel free to reopen and/or comment if there are any follow-ups. Thanks again for raising the issue and constructive comments.