C++ blocking API needs updating Topic is solved

Report here problems in any of ChibiOS components. This forum is NOT for support.
User avatar
Spym
Posts: 45
Joined: Tue Oct 15, 2013 10:20 am
Location: Tallinn, Estonia
Has thanked: 2 times
Been thanked: 3 times
Contact:

C++ blocking API needs updating  Topic is solved

Postby Spym » Thu Apr 19, 2018 1:10 pm

So I am migrating from v16 to v18. My application is written in C++ and it relies on the ChibiOS C++ wrapper. I am using 64-bit sys interval and 32-bit system time; the compiler reported an overflow error:

Code: Select all

error: large integer implicitly truncated to unsigned type [-Werror=overflow]
         (void) waitAnyEventTimeout(ALL_EVENTS, TIME_INFINITE);
                                                             ^


This is because the C++ API has not been updated to use sysinterval_t instead of systime_t:

Code: Select all

  eventmask_t BaseThread::waitOneEventTimeout(eventmask_t ewmask,
                                              systime_t time) {

    return chEvtWaitOneTimeout(ewmask, time);
  }

  eventmask_t BaseThread::waitAnyEventTimeout(eventmask_t ewmask,
                                              systime_t time) {

    return chEvtWaitAnyTimeout(ewmask, time);
  }

  eventmask_t BaseThread::waitAllEventsTimeout(eventmask_t ewmask,
                                               systime_t time) {

    return chEvtWaitAllTimeout(ewmask, time);
  }
 

User avatar
Spym
Posts: 45
Joined: Tue Oct 15, 2013 10:20 am
Location: Tallinn, Estonia
Has thanked: 2 times
Been thanked: 3 times
Contact:

Re: C++ blocking API needs updating

Postby Spym » Thu Apr 19, 2018 1:33 pm

Can we see this fixed in 18.2, please?

User avatar
Giovanni
Site Admin
Posts: 14455
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: C++ blocking API needs updating

Postby Giovanni » Thu Apr 19, 2018 1:56 pm

Yes, it is released code so maintained.

Giovanni

User avatar
Giovanni
Site Admin
Posts: 14455
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: C++ blocking API needs updating

Postby Giovanni » Sat Apr 21, 2018 4:14 pm

Hi,

I committed a fixed wrapper in trunk, I wish to make some other enhancements before porting it back in 18.2. I am also introducing some of the changes from the enhanced C++ wrapper that has been contributed a while ago and never integrated as-is.

You can preview it in trunk.

Giovanni

User avatar
Spym
Posts: 45
Joined: Tue Oct 15, 2013 10:20 am
Location: Tallinn, Estonia
Has thanked: 2 times
Been thanked: 3 times
Contact:

Re: C++ blocking API needs updating

Postby Spym » Sat Apr 21, 2018 5:24 pm

Thanks! I have my own fork where I fix bugs, so I'm going to postpone updating until the changes are fully backported.

N.B. you shouldn't declare inline methods inline explicitly, because per the C++ standard this is redundant:

Code: Select all

    inline void signalEventsI(eventmask_t mask) {
      chEvtSignalI(thread_ref, mask);
    }

Should be:

Code: Select all

    void signalEventsI(eventmask_t mask) {
      chEvtSignalI(thread_ref, mask);
    }


Also, destructors of polymorphic classes must be declared virtual; if you could fix it also that would be great.

Const qualifiers are missing in a lot of places; const-correctness is a lot more important in C++ thanks to the stricter type system:

Code: Select all

    inline bool isArmedI(void) {
      return chVTIsArmedI(&timer_ref);
    }

Should be (note the lack of void also):

Code: Select all

    bool isArmedI() const {
      return chVTIsArmedI(&timer_ref);
    }

User avatar
Giovanni
Site Admin
Posts: 14455
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: C++ blocking API needs updating

Postby Giovanni » Sat Apr 21, 2018 5:37 pm

Hi,

Feel free to post a fixed version of the file, I will proceed from there.

Giovanni

User avatar
Giovanni
Site Admin
Posts: 14455
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: C++ blocking API needs updating

Postby Giovanni » Sun Apr 22, 2018 7:53 am

I removed the inline keywords, the "const" change causes errors. Is it really required anyway? the wrappers just call a C function and everything is handled in there.

I am now adding missing APIs and merging parts of the contributed wrapper.

Giovanni

User avatar
Spym
Posts: 45
Joined: Tue Oct 15, 2013 10:20 am
Location: Tallinn, Estonia
Has thanked: 2 times
Been thanked: 3 times
Contact:

Re: C++ blocking API needs updating

Postby Spym » Sun Apr 22, 2018 3:01 pm

There is a "System" namespace defined in the header file and a "system" namespace (lowercase) in the cpp file. The cpp file only adds complexity to the build process and should be removed, it is not needed at all; all functions can be defined in the header file. In order to define a standalone function in the header file, use the "inline" keyword - it has nothing to do with literal inlining in this case; rather, it informs the linker that the definition of this function may appear in other translation units (because it is included together with the header file).

On a related note, the new naming seems inconsistent - why the root namespace is named "chibios_rt" (lowercase), but the nested namespaces are CamelCased? I would suggest making all lowercase.

Declaring a function inline when it is defined in a different translation unit is pointless because normally the compiler won't be able to inline it anyway (unless some form of link-time optimization is used, although in that case, the compiler will probably ignore the inline keyword anyway).

I am noticing that the wrapper uses constructs from C++11 (e.g. "delete" keyword for special functions), but at the same time, it also makes use of deprecated entities like the NULL constant. Which C++ standard is it supposed to be compatible with? I strongly suggest to stick to C++11, in which case a couple of things will need updating.

Some methods of derived classes (e.g. BaseThread) return an instance of its base class by value. In C++ parlance this is known as "slicing" and, in general, it is extremely dangerous, as it essentially converts objects from one memory layout to another without a proper cast. Seeing as the sliced objects here do not contain any data entries defined in the derived types (unless I'm missing something), this is unlikely to cause any issues, but nevertheless, this is a poor practice. Such occurrences must return references, not values; furthermore, as C++ supports return type covariance, these should be references of the derived type, not the base type. Example:

Code: Select all

virtual ThreadReference start(tprio_t prio) {
    void _thd_start(void *arg);
    thread_ref = chThdCreateStatic(wa, sizeof(wa), prio, _thd_start, this);
    return *this;
}

Correct implementation (the "virtual" keyword is redundant and should be replaced with "override", since we're using C++11 now anyway):

Code: Select all

BaseStaticThread<N>& start(tprio_t prio) override {
    void _thd_start(void *arg);
    thread_ref = chThdCreateStatic(wa, sizeof(wa), prio, _thd_start, this);
    return *this;
}


The BaseThread class defines a virtual method main(), but since the application is supposed to redefine it, the default implementation is meaningless anyway. I suggest having it made pure virtual instead.

On the subject of const qualifiers - they are needed for typesafe API, but it's possible to cope without them (at the cost of somewhat less safe application code). A const-qualified method has its implicit "this" parameter const-qualified as well, which essentially means that such methods cannot modify the inner state of the object (barring some special circumstances). You can't make all of the methods const, obviously; I have skimmed through the wrapper and found only a handful of examples where this would make sense. For example:

Code: Select all

cnt_t getCounterI() {
    return chSemGetCounterI(&sem);
}

A correct implementation must use const, because the inner state is not mutated:

Code: Select all

cnt_t getCounterI() const {
    return chSemGetCounterI(&sem);
}

The above should work well. However, there may exist certain C API functions that are not const-correct, expecting a mutable reference to an object while not mutating its state by design. If that were the case here (it is not, because chSemGetCounterI is just a macro, but just for the sake of example), a hack would be required:

Code: Select all

cnt_t getCounterI() const {
    return chSemGetCounterI(const_cast<semaphore_t*>(&sem));
}


I have attached an edited version of the wrapper based on this revision https://github.com/ChibiOS/ChibiOS/blob ... ers/ch.hpp. I have not tested it, though. I wish I had time to fix everything, but there's too much on my plate already. I'd be happy to assist via the forum though!

C++ is a mess.
Attachments
ch.tar.gz
(13.61 KiB) Downloaded 144 times

User avatar
Giovanni
Site Admin
Posts: 14455
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: C++ blocking API needs updating

Postby Giovanni » Mon Apr 23, 2018 1:50 pm

Hi,

Some notes:

- NULL comes from the underlying C code, I think it cannot be replaced.
- ThreadReference is no more an ancestor of BaseThread in latest code, probably you are looking at the old one.
- I reverted all namespaces in classes, camel case is consistent with class names.
- I am not sure if const does really anything all methods are just calling a C function that is not const-aware.

Applied some of the suggestions.

Giovanni

User avatar
Spym
Posts: 45
Joined: Tue Oct 15, 2013 10:20 am
Location: Tallinn, Estonia
Has thanked: 2 times
Been thanked: 3 times
Contact:

Re: C++ blocking API needs updating

Postby Spym » Wed Apr 25, 2018 1:46 pm

Thanks, Giovanni.

Giovanni wrote:- NULL comes from the underlying C code, I think it cannot be replaced.

nullptr is backwards compatible with the NULL pointer. NULL should be replaced with nullptr in the wrapper in order to take advantages of the extra type safety and code correctness guarantees implemented in C++11. Here's a relevant quote from Wikipedia:
Wikipedia wrote:C++11 corrects this by introducing a new keyword to serve as a distinguished null pointer constant: nullptr. It is of type nullptr_t, which is implicitly convertible and comparable to any pointer type or pointer-to-member type.


Giovanni wrote:- ThreadReference is no more an ancestor of BaseThread in latest code, probably you are looking at the old one.

Yes, it is an acestor, and what I was trying to say (sorry if my explanation was confusing) is that you can't slice a derived class into the base class type - this practice is extremely dangerous and may lead to subtle errors. You can gain more info at https://stackoverflow.com/questions/274 ... ct-slicing. This should be fixed by replacing the return type from ThreadReference to the derived type reference as shown in the example I posted earlier.

Giovanni wrote:- I am not sure if const does really anything all methods are just calling a C function that is not const-aware.

Const is still important. You shouldn't look at the C API, that's just an implementation detail the user of the wrapper doesn't need to care about. Rather, look upwards - the user must be able to invoke, for example, the method getCounterI() on a const reference, because the method is not mutating. Currently it is not possible because the method is not const-correct. This is a bug. More info: https://stackoverflow.com/questions/136 ... orrectness


Return to “Bug Reports”

Who is online

Users browsing this forum: No registered users and 15 guests