Go Down

Topic: "Correct" method for migrating libs to Arduino 1.0 (Read 5050 times) previous topic - next topic

ajfisher

HI all,

Apologies if this has been covered elsewhere but couldn't seem to find something specific about consensus for migrating libraries to 1.0.

I have several libraries that I'm now looking to move to 1.0 given the official release recently. Given that there will probably be a lag on people upgrading (eg currently no binary upgrades in Debian or Ubuntu for auto-upgrading) I want to ensure I have a version that will work in Auduino 0023 (and similar) for at least a period of time.

My current thinking is to simply take my current code, move working versions across to a pre-1.0 folder or preface the files with that and then the default code will work in 1.0

EG:

Old lib was:

/header.h
/code.cpp
/examples/

This would change to:

/header.pre-1.0.h
/header.h
/code.pre-1.0.cpp
/code.cpp
/examples-pre-1.0/
/examples

This code can then be retired at a later stage in a few months etc.

Other options include branches in git but it's probably not wise to assume familiarity with git to understand what's going on.

Has there been much consensus on the right way to approach this for best compatibility and maintainability whilst the population upgrades.

Cheers
ajfisher


No, I wouldn't do that.  You will have compile errors all over the place if you do that.

The general approach seems to be to keep a single library that compiles under BOTH 0023 and 1.0.  You'll see something like this at the top:

Code: [Select]

#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif


And then in other places where the behaviour is different under 0023, you can have some more "#if ARDUINO < 100"

The other reasonable approach would be to have two separate libraries, like this...

~/Source/Arduino/
  SuperLib/
    SuperLib.h
    SuperLib.cpp
    helpers.cpp
  SuperLib-1.0/
    SuperLib-1.0.h
    SuperLib.cpp
    helpers.cpp

You could conceivably make them branches in git, but that sure makes it harder for people to find if they're not used to git branches.  You could prepare two separate downloads of course, SuperLib.zip and SuperLib-1.0.zip.

ajfisher

Thanks for that.

I was sort of leaning towards the #if arduino < 100 option however the readability suffers a lot when you have code that is making use of the changes (eg .write instead of .print changes etc).

I think as there appears to be no real "correct" method that a combination of the below is probably the way to do it with the git branches + downloads being the way to deal with the most "messy" of cases where there's a lot of changes.

Cheers
ajfisher


Graynomad

The #if option can produce unreadable code. I suggest if you have two many in a given function (ie > 2-3 probably) that you have two versions of the function and wrap them in an #if. That way each one is readable.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

Yah.  For my fork of RTClib I did this...

Code: [Select]

#if ARDUINO < 100
#define SEND(x) send(x)
#define RECEIVE(x) receive(x)
#else
#define SEND(x) write(static_cast<uint8_t>(x))
#define RECEIVE(x) read(x)
#endif

...

Wire.SEND(0);
Wire.SEND(bin2bcd(dt.second()));
Wire.SEND(bin2bcd(dt.minute()));
Wire.SEND(bin2bcd(dt.hour()));
Wire.SEND(bin2bcd(0));


There are more elegant ways, but that did the job fast :)

pYro_65

or create a file 'WProgram.h' in the 1.0 core directory 'D:\PathToArduino\hardware\arduino\cores\arduino'

and put inside it

Code: [Select]

#include "Arduino.h"


and just for gcc compiler optimisation you could also put


Code: [Select]

#ifndef HEADER_WPROGRAM
  #define HEADER_WPROGRAM
  #include "Arduino.h"
#endif


this will fix most libraries.

liudr

I am having some trouble with the lcd.write method. I had no problem with this method and now with 1.0

Error: " candidates are: virtual size_t LiquidCrystal::write(uint8_t)"

So what should I do? Change all write to print?

PaulS

Quote
So what should I do? Change all write to print?

Altogether now: "Post the code that generates the error!"

liudr


Quote
So what should I do? Change all write to print?

Altogether now: "Post the code that generates the error!"


Code: [Select]

#include <LiquidCrystal.h>

LiquidCrystal LCD(2,3,4,5,6,7);
void setup()
{
  LCD.begin(16,2);
  LCD.write((uint8_t)'\0');
}
void loop()
{}


The above code, when compiled in 1.0, only works with the type case (uint8_t) but works without it in 0022. I traced the library code to the following:

arduino 0022 LiquidCrystal.cpp:
Code: [Select]
inline void LiquidCrystal::write(uint8_t value) {
  send(value, HIGH);
}


arduino 1.0 LiquidCrystal.cpp:
Code: [Select]
inline size_t LiquidCrystal::write(uint8_t value) {
  send(value, HIGH);
  return 1; // assume sucess <- TYPO Anyone proofread the library???
}


I'm stuck on why using write('\0') won't work in 1.0

The LiquidCrystal.h is fine but 0022 and 1.0

There must be a type cast in 0022 that casts all (const char) into uint8_t in some place that the 1.0 is missing, right?

#9
Dec 20, 2011, 07:37 am Last Edit: Dec 20, 2011, 07:41 am by maniacbug Reason: 1
The cause here is something SUPER subtle.  It really should never have worked. The full text of the error message is listed below.  Indeed, it tells the truth.  It cannot tell if you mean to call 'write' with '0' as a uint8_t or 'write' with '0' as a null pointer.

Code: [Select]

something.pde:66: error: call of overloaded 'write(int)' is ambiguous
/opt/Arduino/master/libraries/LiquidCrystal/LiquidCrystal.h:82: note: candidates are: virtual size_t LiquidCrystal::write(uint8_t)
/opt/Arduino/master/hardware/arduino/cores/arduino/Print.h:49: note:                 size_t Print::write(const char*)


This is going on my list of interview questions :)  The super subtle difference is that in 1.0, Print::write(const char *str) is no longer virtual.  In 0022, it's virtual.  So in 0022, the compiler is trying to resolve what you mean by LiquidCrystal::write(0).  As there is only one method actually declared as a LiquidCrystal member, it can pick write(uint8_t).  It can de-prioritize the virtual write(const char*) that is only in the base class. However, in 1.0, it's more confusing, because write(const char*) is a first-class member of LiquidCrystal due to it not being virtual.  So the compiler's choice is no longer obvious.

Anyway, you're in the clear for library compat.  Just use (uint8_t)0 and you'll be safe in both cases.


liudr

Quite a bit subtle here. I guess this is the actual difference:

Print.h in 0022
    virtual void write(const char *str); // Not defined in LiquidCrystal.cpp so unusable by compiler

Print.h in 1.0
    size_t write(const char *str) { return write((const uint8_t *)str, strlen(str)); } // Defined in base class so useable by compiler

So the write method with a pointer as parameter is actually NOT a virtual method and has its code, ALTHOUGH, it calls a virtual method (write(const uint8_t *buffer, size_t size)) that is not defined in LiquidCrystal.cpp of either version.

So this little bug is making the compiler ponder whether to call write (const char *) or write (uint8_t), only when a zero is supplied, because other numbers would only be valid when converted into uint8_t, not a pointer but zero is OK for both types?! Just checked with other numbers and it compiles in 1.0 without (uint8_t). Am I passing your interview-round 1? BTW, what is the meaning of _t? I can understand the unsigned 8-bit integer part but not the _t. Thanks!

sixeyes

I've always assumed the _t bit in C/C++ was for type.

Iain


Print.h in 0022
    virtual void write(const char *str); // Not defined in LiquidCrystal.cpp so unusable by compiler

So the write method with a pointer as parameter is actually NOT a virtual method and has its code, ALTHOUGH, it calls a virtual method (write(const uint8_t *buffer, size_t size)) that is not defined in LiquidCrystal.cpp of either version.


Not quite.  When the compiler is working on your sketch file, the one with "LCD.write(0)" in it, it doesn't know about LiquidCrystal.cpp or Print.cpp for that matter.  It only knows what the headers tell it.  Print::write(const char*) is definitely a virtual function.  It says so right there in the header :)


BTW, what is the meaning of _t? I can understand the unsigned 8-bit integer part but not the _t. Thanks!


C99 defined standard fixed-size types, including uint8_t.  The suffix "_t" has long been taken to designate a symbol which represents a type.  I've never actually seen a written standard which said so, but that's the convention.  The idea behind it, and any naming convention really, is so that you can look at a symbol and KNOW what kind of symbol it is.  The extreme of this is Hungarian notation, which seems to have fallen out of favor a bit.

Anyway you could do something like this:

Code: [Select]

typedef unsigned long something_t;
something_t something;


... to define variable 'something' to be an instance of 'something_t'.

liudr

Thanks! I think I got this part sorted out. Any other stuff that we should be watching out while migrating the libraries?

mellis

BTW, Serial.write(byte) (and other Print sub-classes) should work in 0022 /0023 as well as 1.0.  So if you have code that currently does Serial.print(n, BYTE), you should be able to change it to Serial.write() and maintain compatibility. 

Go Up