Skip to content

Add cv-qualifiers to Callback and deprecate combinatorial explody functions #2496

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 8, 2016

Conversation

geky
Copy link
Contributor

@geky geky commented Aug 18, 2016

From this discussion #2395 and the related issue #2490

The combination of cv-qualifiers with callback overloads results in a large number of overloads to match all possible types that could be passed to the Callback class.

This isn't a big problem for void (*)(cv T *) function types, but for void (T::*)() cv types, the method in which cv-qualifiers are associated prevents encoding the cv-qualifiers in the T type, and requiring a large combination of overloads for all cv-qualifiers.

This pr involves several changes:

  • Adds full support for cv-qualifiers in Callback constructor
  • Provides convenience mbed::callback function to pass different arguments to Callback with infered type.
  • Deprecates existing convenience functions to avoid confusion if the user attempts to use a const or volatile object with the existing convenience functions.
  • Updates tests appropriately

Supported overloads:

callback(void (*f)(A...));
callback(const Callback<R(A...)> &);
callback(T *t,                void (*f)(T*, A...));
callback(const T *t,          void (*f)(const T*, A...));
callback(volatile T *t,       void (*f)(volatile T*, A...));
callback(const volatile T *t, void (*f)(const volatile T*, A...));
callback(T *t,                void (T::*f)(A...));
callback(const T *t,          void (T::*f)(A...) const);
callback(volatile T *t,       void (T::*f)(A...) volatile);
callback(const volatile T *t, void (T::*f)(A...) const volatile);

Should fix: #2490
Replaces: #2395 and #2495
cc @pan-, @0xc0170

Let me know any thoughts/suggestions on this solution you guys have.

@pan-
Copy link
Member

pan- commented Aug 19, 2016

That's a really nice change, I have few comments to make:

  • It would be interesting to make call and operator() const like in std::function, it allow user code to pass callback around with the guarantee that it will not be modified.
  • The operator= is missing.
  • Maybe it would be interesting to add external operators == and !=.
  • The call function might not be instantiated if R can't be constructed or casted from the literal 0 (see here).
  • Please provide relevant comments for every overload and template class specialization. It will help a lot with online documentation. Otherwise, user might get nothing if the overload is not documented or not useful comment like Templated function class.
  • It should be indicated that the file Callback.h is auto generated and should not be modified manually.

@geky
Copy link
Contributor Author

geky commented Aug 19, 2016

+1, these are good points

Since a copy involves a simple copy of all the members, is the default operator= not sufficient?

Unfortunately the cast is for backwards compatibility, since at some point someone thought FunctionPointer f; f(); should be well defined. I can update it to value initialize R without breaking backwards compatibility, although that still assumes more about R than should be necessary.

@pan-
Copy link
Member

pan- commented Aug 19, 2016

Since a copy involves a simple copy of all the members, is the default operator= not sufficient?

Yes it is sufficient but in that case drop the copy construction operator (rule of 0 or rule of 3 (five in c++11)).

Unfortunately the cast is for backwards compatibility, since at some point someone thought FunctionPointer f; f();

The question is what we should do in that case ?
Instead of returning something totally invalid maybe it can be trapped into an error.

    R call() {
        if (!_thunk) {
            // never returns
            error("LOGIC_ERROR: Call to a not initialized callback!");
        }
        return _thunk(_obj, &_func);
    }

So FunctionPointer f; f(); is still valid, the user is notified in such case and there is template instantiation issues.

@geky
Copy link
Contributor Author

geky commented Aug 19, 2016

The reason for explicitely defining the copy constructor was to make it clear that the Callback class could be copied safely. Tangentally do we expect this to work? Might need the assignment operator:

Callback<void()> cb;
void doit();
cb = doit;

(Still looking into where null callback is used)

@geky
Copy link
Contributor Author

geky commented Aug 19, 2016

Well I can't seem to find where the null callback was relied on, I thought it was somewhere in our codebase. I went ahead and moved the null check into the deprecated FunctionPointer class to maintain that backwards compatibility. I tested a few things locally and we'll see if tests find any issues.

Interestingly enough, the operator= seems to work fine with C style function pointers at the moment, I think this is because it infers the cast before the assignment. I went ahead and added a test for that.

I'd prefer to keep the copy constructor since it clarifies the class can be safely copied and matches the overloads for attach and callback, though I can remove it if we really want it gone.

@geky
Copy link
Contributor Author

geky commented Aug 19, 2016

Looks like it is a bit tricky, since several drivers are relying on uninitialized Callbacks being callable, however this is an easy fix.

@pan-
Copy link
Member

pan- commented Aug 22, 2016

Interestingly enough, the operator= seems to work fine with C style function pointers at the moment, I think this is because it infers the cast before the assignment. I went ahead and added a test for that.

Callback function pointer constructors are not tagged with explicit and therefore act as conversion constructors (more here). This is the expected behavior.

I'd prefer to keep the copy constructor since it clarifies the class can be safely copied and matches the overloads for attach and callback, though I can remove it if we really want it gone.

@0xc0170 @sg- Do we recommend any pattern like rule of three or rule of zero for C++ value types ?
What is the best approach here in term of readability and developer experience ?

Otherwise, +1 for the whole PR.

@0xc0170
Copy link
Contributor

0xc0170 commented Aug 23, 2016

@0xc0170 @sg- Do we recommend any pattern like rule of three or rule of zero for C++ value types ?
What is the best approach here in term of readability and developer experience ?

+1, this should be covered in the guidelines. I like a rule "all or nothing". We already provide "rule of three" or at least partially in our classes, we manage resources, thus inclined to rule "all", for us (c++03) three. If we agree, Callback should follow it.

The changes look good. People happy with jinja2 generated files?

@sg-
Copy link
Contributor

sg- commented Aug 23, 2016

I like a rule "all or nothing"

👍

The changes look good. People happy with jinja2 generated files?

nope

@geky
Copy link
Contributor Author

geky commented Aug 23, 2016

@sg-, is there an alternative template engine we should be using? m4 perhaps?

Or do you suggest just not committing the template files?

@sg-
Copy link
Contributor

sg- commented Aug 23, 2016

Not committing the template files. If something needs to change that often to need auto-generation, I don't think its ready for master :)

@geky
Copy link
Contributor Author

geky commented Aug 24, 2016

Copy contructor and jinja templates removed, let me know any other feedback.

@@ -1,3 +1,6 @@
// Note: This file was autogenerated from main.j2
// j2 main.j2 > main.h
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not true anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

@0xc0170
Copy link
Contributor

0xc0170 commented Aug 24, 2016

LGTM (one comment not valid anymore)

@geky geky force-pushed the callback-cv branch 2 times, most recently from 0c8ec15 to abd5430 Compare August 24, 2016 15:58
@geky
Copy link
Contributor Author

geky commented Aug 24, 2016

Should be good to go 👍

@geky
Copy link
Contributor Author

geky commented Aug 24, 2016

/morph test

@mbed-bot
Copy link

Result: FAILURE

Your command has finished executing! Here's what you wrote!

/morph test

Output

mbed Build Number: 701

Build failed!

@geky
Copy link
Contributor Author

geky commented Aug 24, 2016

Looks like all the template expansions in the test caused flash to exceed 64K : /

After talking with @bridadan (correct me if I'm wrong), will split the test into parts and move to TESTS/mbed_functional, which can house other non-hardware apis.

@geky
Copy link
Contributor Author

geky commented Aug 25, 2016

/morph test

@pan-
Copy link
Member

pan- commented Aug 25, 2016

Looks like all the template expansions in the test caused flash to exceed 64K : /

At a latter stage maybe we can put all the data members in a non template base class and have function to set and reset them.

That way operator bool can be non template and reset and assignment can be non template.

It is also possible to factorize all operator== and operator!= instantiation, it is just a memcmp of this base class.

@mbed-bot
Copy link

Result: SUCCESS

Your command has finished executing! Here's what you wrote!

/morph test

Output

mbed Build Number: 715

All builds and test passed!

As the templated tests grew, the resulting binary exceeded a
flash size of 64K. This caused the test to incorrectly fail on
small devices.

Moved and split into the following:
TESTS/mbed_functional/callback
TESTS/mbed_functional/callback_small
TESTS/mbed_functional/callback_big
TESTS/mbed_functional/functionpointer
@geky
Copy link
Contributor Author

geky commented Aug 26, 2016

/morph test

@mbed-bot
Copy link

Result: ABORTED

Your command has finished executing! Here's what you wrote!

/morph test

Output

mbed Build Number: 728

Build Prep failed!

@bridadan
Copy link
Contributor

@geky Sorry, making some CI changes. Please hold off on firing more PRs for the moment

@geky
Copy link
Contributor Author

geky commented Aug 29, 2016

/morph test

@mbed-bot
Copy link

Result: FAILURE

Your command has finished executing! Here's what you wrote!

/morph test

Output

mbed Build Number: 741

Test Prep failed!

@bridadan
Copy link
Contributor

Looks like there was bug introduced with the latest version of setuptools, oh joy! I've deployed a workaround that should fix the test prep issue for now until they patch setuptools.

/morph test

@mbed-bot
Copy link

Result: FAILURE

Your command has finished executing! Here's what you wrote!

/morph test

Output

mbed Build Number: 742

Test failed!

@geky
Copy link
Contributor Author

geky commented Aug 29, 2016

/morph test

@mbed-bot
Copy link

Result: FAILURE

Your command has finished executing! Here's what you wrote!

/morph test

Output

mbed Build Number: 745

Test failed!

@bridadan
Copy link
Contributor

@geky Test failures look ok 👍 Just timing issues again from the CI machine being under load

@geky
Copy link
Contributor Author

geky commented Aug 30, 2016

Ah ok, in that case this pr should be good to go

LGTM?

@pan-
Copy link
Member

pan- commented Sep 7, 2016

LGTM ?

@teetak01
Copy link
Contributor

@yogpan01
Copy link
Contributor

@sg- @geky @bridadan Why did you ignore #2653 because this PR has now broken the whole mbed-client builds ?
Please revert this PR ASAP or push the fix to resolve this. All our CI build jobs for mbed CLient are now broken and team is blocked because of this.
@jupe @marhil01 @tommikas FYI

@jupe
Copy link
Contributor

jupe commented Sep 10, 2016

we need also this #2656 to be merged asap because it could find this kind of issues.. but I'm wondering why any other CI job's didn't recognize this before it was too late..?

@yogpan01
Copy link
Contributor

Our nightly build job identified this issue , it has been failing since yesterday morning after this PR has been merged.

@jupe
Copy link
Contributor

jupe commented Sep 10, 2016

i think this kind of issues should be identified during PR verification jobs - before merging..

@yogpan01
Copy link
Contributor

mbed-os needs to add mbed-client build jobs to the PR verification queue.

@sg-
Copy link
Contributor

sg- commented Sep 10, 2016

@sg- @geky @bridadan Why did you ignore #2653 because this PR has now broken the whole mbed-client builds ?
Please revert this PR ASAP or push the fix to resolve this. All our CI build jobs for mbed CLient are now broken and team is blocked because of this.
@jupe @marhil01 @tommikas FYI

Will patch or revert. Wasnt aware of this issues as it wasnt linked to the pr until after the merge

@sg-
Copy link
Contributor

sg- commented Sep 10, 2016

i think this kind of issues should be identified during PR verification jobs - before merging..

There is no verification job on that catches this. They're missing from the jenkinsfile

@sg-
Copy link
Contributor

sg- commented Sep 10, 2016

we need also this #2656 to be merged asap because it could find this kind of issues.. but I'm wondering why any other CI job's didn't recognize this before it was too late..?

This has failing status so I cant merge 👎

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Avoid combinatorial explosion in callback overloads
9 participants