Suggestions for TMP/function introspection problem?

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Suggestions for TMP/function introspection problem?

Boost - Dev mailing list
Hi all,

I am trying to figure out how to solve an introspection problem since a few
days, and I am not making progress. I could use some help, it is for
boost.histogram. Maybe what I want is not possible...

Here is my problem. I have some classes A, B, ... which have some
overloaded methods, let's call them `foo`.

struct A {
void foo(int, char);
void foo(bool, int, char);
};

struct B {
void foo(double);
void foo(bool, double);
};

I need a way to detect how many arguments the shorter methods has. If the
shorter method is foo(Ts...), the longer method is always (bool, Ts...).
This is guaranteed. I do not know what Ts... is, it can be anything.

I cannot use boost::callable_traits, because an overloaded member function
cannot be fetched by name

using t = boost::callable_traits::args_t<decltype(&A::foo)>; // fails,
overload is ambiguous

Can't I match the member function with a template of the form
something(bool, Ts...) somehow?

Any ideas?

Best regards,
Hans

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Suggestions for TMP/function introspection problem?

Boost - Dev mailing list
AMDG

On 11/07/2018 01:18 PM, Hans Dembinski via Boost wrote:
>
> I am trying to figure out how to solve an introspection problem since a few
> days, and I am not making progress. I could use some help, it is for
> boost.histogram. Maybe what I want is not possible...
>

I don't know what you need this for, but
I would recommend reconsidering why you
need this in the first place.

> Here is my problem. I have some classes A, B, ... which have some
> overloaded methods, let's call them `foo`.
>
> struct A {
> void foo(int, char);
> void foo(bool, int, char);
> };
>
> struct B {
> void foo(double);
> void foo(bool, double);
> };
>
> I need a way to detect how many arguments the shorter methods has. If the
> shorter method is foo(Ts...), the longer method is always (bool, Ts...).
> This is guaranteed. I do not know what Ts... is, it can be anything.
>

What if Ts... has a bool as the first item?

> I cannot use boost::callable_traits, because an overloaded member function
> cannot be fetched by name
>
> using t = boost::callable_traits::args_t<decltype(&A::foo)>; // fails,
> overload is ambiguous
>
> Can't I match the member function with a template of the form
> something(bool, Ts...) somehow?
>

Like this?

template<class R, class C, class... T>
constexpr int arity_impl(R (C::*)(bool, T...))
{ return sizeof...(T); }

template<int N>
struct int_ {};

struct Tester {
    void fun(double);
    void fun(bool, double);
};

int main() {
    int_<arity_impl(&Tester::fun)> x;
}

In Christ,
Steven Watanabe

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Suggestions for TMP/function introspection problem?

Boost - Dev mailing list
Hi Steven,

> On 7. Nov 2018, at 21:38, Steven Watanabe via Boost <[hidden email]> wrote:
>
> On 11/07/2018 01:18 PM, Hans Dembinski via Boost wrote:
>>
>> I am trying to figure out how to solve an introspection problem since a few
>> days, and I am not making progress. I could use some help, it is for
>> boost.histogram. Maybe what I want is not possible...
>>
>
> I don't know what you need this for, but
> I would recommend reconsidering why you
> need this in the first place.

It is for generic support of arbitrary accumulators per bin. Normally, a histogram gets some values equal to the number of axes, finds a corresponding bin and increments a counter for that bin. More generally, one can replace the counter by an accumulator that can accept an arbitrary number of values (the standard histogram is then the special case where the accumulator accepts zero arguments). Extra arguments passed to the histogram are forwarded to the accumulator. For example, an accumulator could compute a mean for values which end up in the same bin. This is what people in high energy physics call a "profile".

// bin in 'x', compute a mean of 'y' for each bin in 'x'
auto h = make_histogram_with(std::vector<mean_accumulator>(), axis::regular<>(10, 0, 1));

// first argument is used to find the bin, second argument is passed to the mean_accumulator instance of that bin
h(1, 2);

The operator() of the accumulator could accept more than one value. Maybe you want to bin in 'x' and compute means for 'y' and 'z' at the same time. This should work and then you would pass three values to the histogram in total. I want to use the introspection for the accumulator to detect the number of arguments. I don't always know at compile-time how many arguments should be passed to the histogram axes (because one may use a dynamic histogram where the number of axis is only known at runtime), so I cannot just subtract the arguments that go to the axes and push the rest to the accumulator. But I could do it the other way round, subtract the arguments that need to go to the accumulator and pass the rest to the histogram axes.

>> Here is my problem. I have some classes A, B, ... which have some
>> overloaded methods, let's call them `foo`.
>>
>> struct A {
>> void foo(int, char);
>> void foo(bool, int, char);
>> };
>>
>> struct B {
>> void foo(double);
>> void foo(bool, double);
>> };
>>
>> I need a way to detect how many arguments the shorter methods has. If the
>> shorter method is foo(Ts...), the longer method is always (bool, Ts...).
>> This is guaranteed. I do not know what Ts... is, it can be anything.
>>
>
> What if Ts... has a bool as the first item?

This case can never happen. bool was a placeholder for the special weight_type. It never makes sense to pass two weight_type's to an accumulator.

>> Can't I match the member function with a template of the form
>> something(bool, Ts...) somehow?
>
> Like this?
>
> template<class R, class C, class... T>
> constexpr int arity_impl(R (C::*)(bool, T...))
> { return sizeof...(T); }
>
> template<int N>
> struct int_ {};
>
> struct Tester {
>    void fun(double);
>    void fun(bool, double);
> };
>
> int main() {
>    int_<arity_impl(&Tester::fun)> x;
> }

Yes, great, thanks.

Hans


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Suggestions for TMP/function introspection problem?

Boost - Dev mailing list
AMDG

On 11/08/2018 10:27 AM, Hans Dembinski wrote:

>> On 7. Nov 2018, at 21:38, Steven Watanabe via Boost <[hidden email]> wrote:
>>
>> On 11/07/2018 01:18 PM, Hans Dembinski via Boost wrote:
>>>
>>> I am trying to figure out how to solve an introspection problem since a few
>>> days, and I am not making progress. I could use some help, it is for
>>> boost.histogram. Maybe what I want is not possible...
>>>
>>
>> I don't know what you need this for, but
>> I would recommend reconsidering why you
>> need this in the first place.
>
> It is for generic support of arbitrary accumulators per bin. Normally, a histogram gets some values equal to the number of axes, finds a corresponding bin and increments a counter for that bin. More generally, one can replace the counter by an accumulator that can accept an arbitrary number of values (the standard histogram is then the special case where the accumulator accepts zero arguments). Extra arguments passed to the histogram are forwarded to the accumulator. For example, an accumulator could compute a mean for values which end up in the same bin. This is what people in high energy physics call a "profile".
>
> // bin in 'x', compute a mean of 'y' for each bin in 'x'
> auto h = make_histogram_with(std::vector<mean_accumulator>(), axis::regular<>(10, 0, 1));
>
> // first argument is used to find the bin, second argument is passed to the mean_accumulator instance of that bin
> h(1, 2);
>
> The operator() of the accumulator could accept more than one value. Maybe you want to bin in 'x' and compute means for 'y' and 'z' at the same time. This should work and then you would pass three values to the histogram in total. I want to use the introspection for the accumulator to detect the number of arguments.

Okay.  So the problem is that you're essentially
packing two variadic argument lists together and
you need to untangle them.  I don't think that's
a good idea in the first place, as it makes the
meaning of user code unclear.  I think it would
be better to distinguish them at the call-site
somehow.

> I don't always know at compile-time how many arguments should be passed to the histogram axes (because one may use a dynamic histogram where the number of axis is only known at runtime), so I cannot just subtract the arguments that go to the axes and push the rest to the accumulator. But I could do it the other way round, subtract the arguments that need to go to the accumulator and pass the rest to the histogram axes.
>

In a dynamic histogram, why can't the accumulator
also be dynamic?

In Christ,
Steven Watanabe

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Suggestions for TMP/function introspection problem?

Boost - Dev mailing list

> On 8. Nov 2018, at 19:03, Steven Watanabe via Boost <[hidden email]> wrote:
>>
>> It is for generic support of arbitrary accumulators per bin. Normally, a histogram gets some values equal to the number of axes, finds a corresponding bin and increments a counter for that bin. More generally, one can replace the counter by an accumulator that can accept an arbitrary number of values (the standard histogram is then the special case where the accumulator accepts zero arguments). Extra arguments passed to the histogram are forwarded to the accumulator. For example, an accumulator could compute a mean for values which end up in the same bin. This is what people in high energy physics call a "profile".
>>
>> // bin in 'x', compute a mean of 'y' for each bin in 'x'
>> auto h = make_histogram_with(std::vector<mean_accumulator>(), axis::regular<>(10, 0, 1));
>>
>> // first argument is used to find the bin, second argument is passed to the mean_accumulator instance of that bin
>> h(1, 2);
>>
>> The operator() of the accumulator could accept more than one value. Maybe you want to bin in 'x' and compute means for 'y' and 'z' at the same time. This should work and then you would pass three values to the histogram in total. I want to use the introspection for the accumulator to detect the number of arguments.
>
> Okay.  So the problem is that you're essentially
> packing two variadic argument lists together and
> you need to untangle them.  I don't think that's
> a good idea in the first place, as it makes the
> meaning of user code unclear.  I think it would
> be better to distinguish them at the call-site
> somehow.

Agreed, this solution is also more readable on the user-facing call-site. In the develop branch, it is now implemented like this (same histogram example as before)

// `sample` is a helper function which stores arguments passed to it as a std::tuple, to be forwarded to the accumulator
h(1, sample(2));

// sample may appear at the beginning or the end and can be combined in arbitrary ways with `weight`
// as long as the values passed to the histogram axis appear in one continuous block
h(weight(3.5), 1, sample(2));
h(1, weight(3.5), sample(2));
h(1, sample(2), weight(3.5));

>> I don't always know at compile-time how many arguments should be passed to the histogram axes (because one may use a dynamic histogram where the number of axis is only known at runtime), so I cannot just subtract the arguments that go to the axes and push the rest to the accumulator. But I could do it the other way round, subtract the arguments that need to go to the accumulator and pass the rest to the histogram axes.
>>
>
> In a dynamic histogram, why can't the accumulator
> also be dynamic?

Good point, that argument was the final nail in the coffin of my idea to make it work without the `sample` helper function.

Thank you for the valuable input!
Hans

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost