C library

i thought i figured the technique of writing and calling C library. But apparently i was too quick.

What am i trying to achieve
i’m trying to write a C library and call it’s functions from the .ino code.

What is my problem?
I can NOT understand how to write a C library for Arduino. And i do not understand why other standard C libraries ( files with .c extension) work and mine is not.

What is the code?

//test.ino

#ifdef __cplusplus
extern "C"{
	#endif
	#include <aaa.h>
	#ifdef __cplusplus
} ;
#endif

int led = 13;

void setup() {
  // put your setup code here, to run once:
  pinMode(led, OUTPUT);
  Serial.begin(9600);
}

void loop() {
    // led = ardprintf('Q');
    for(int i=0; i< 10; i++){
      Serial.print(aaa(10)); 
    	delay(200);
    }

}

aaa.h

int aaa(int a);

aaa.c

#include <Arduino.h>
#include <HardwareSerial.h>
#include "aaa.h"

int aaa(int a){

	Serial.print("Im here");
	return 0;
}

What is the error?

[Stino - Start building "test"...]
[  3%] Creating /tmp/Stino_build/test/test.ino.cpp.o...
"avr-g++" -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD  -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=165 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR   "-I/home/az/work/Arduino/Sketches/Examples/test" "-I/home/az/bin/arduino-1.6.5-r5/hardware/arduino/avr/cores/arduino" "-I/home/az/bin/arduino-1.6.5-r5/hardware/arduino/avr/variants/standard" "-I/home/az/work/Arduino/Sketches/Examples/libraries/aaa" "/tmp/Stino_build/test/test.ino.cpp" -o "/tmp/Stino_build/test/test.ino.cpp.o"
[  6%] Creating /tmp/Stino_build/test/lib_aaa/aaa.c.o...
"avr-gcc" -c -g -Os -w -ffunction-sections -fdata-sections -MMD  -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=165 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR   "-I/home/az/work/Arduino/Sketches/Examples/test" "-I/home/az/bin/arduino-1.6.5-r5/hardware/arduino/avr/cores/arduino" "-I/home/az/bin/arduino-1.6.5-r5/hardware/arduino/avr/variants/standard" "-I/home/az/work/Arduino/Sketches/Examples/libraries/aaa" "/home/az/work/Arduino/Sketches/Examples/libraries/aaa/aaa.c" -o "/tmp/Stino_build/test/lib_aaa/aaa.c.o"
In file included from /home/az/bin/arduino-1.6.5-r5/hardware/arduino/avr/cores/arduino/Print.h:27:0,
                 from /home/az/bin/arduino-1.6.5-r5/hardware/arduino/avr/cores/arduino/Stream.h:26,
                 from /home/az/bin/arduino-1.6.5-r5/hardware/arduino/avr/cores/arduino/HardwareSerial.h:29,
                 from /home/az/work/Arduino/Sketches/Examples/libraries/aaa/aaa.c:2:
/home/az/bin/arduino-1.6.5-r5/hardware/arduino/avr/cores/arduino/Printable.h:25:1: error: unknown type name 'class'
 class Print;
 ^
/home/az/bin/arduino-1.6.5-r5/hardware/arduino/avr/cores/arduino/Printable.h:33:1: error: unknown type name 'class'
 class Printable

<skip>

/home/az/work/Arduino/Sketches/Examples/libraries/aaa/aaa.c: In function 'aaa':
/home/az/work/Arduino/Sketches/Examples/libraries/aaa/aaa.c:7:8: error: request for member 'print' in something not a structure or union
  Serial.print('Im here');

What works?
If i do NOT use Serial ( in other words, if i don’t use <HardwareSerial.h> ), and just have some simple function like:

int aaa(int a){

	return a*a ;
}

code and library compiles and all works fine.

What else works?
if i rename the aaa.c to aaa.cpp and remove the “external C” from the test.ino - everything works great.

You're including C++ classes in your C library. Drop the C idea and write in C++. (You sort of already discovered this.)

i guess so...

But why are there many C files in the distribution then?

AZZ:
i guess so...

But why are there many C files in the distribution then?

If your code compiles fine when you rename it *.cpp, does it really matter why there are so many C files in libraries?

I'm no expert on C / C++ compatibility, but more than likely C++ is intended to be backward-compatible. ie You can probably include C code in a C++ app, but not the other way around.
Someone who's more of an expert might be able to confirm/deny this.

Are you doing anything in your code that's OK in C but not OK in C++? Generally speaking, the code 'you' write will probably be the same regardless of what you call the file. Just go on writing exactly what you planned, but call it *.cpp.

If you're using some things from C like malloc() and free(), it'll compile OK I'd say, and if there are dramas it's not a big step to change and use the C++ new and delete instead.

In the case of Serial, (a class), an error is probably generated in a C file because classes are purely a C++ thing. C knows nothing about classes.

Edit: Of course, I could be completely wrong, but that's how I see it.

I just did a quick test, writing a small class in a C file and got the error "unknown type name 'class'"

Renamed the file with a .cpp extension and it compiled without problems. So C++ classes are definitely not on in a C file.

You can probably include C code in a C++ app, but not the other way around.

Correct.

But, why would you want to go to the hassle of creating a c file to be used in a cpp file, when adding two letters to the file name makes the whole problem go away?

But why are there many C files in the distribution then?

Because they do things that do not require classes or the use of instances of classes. If you don't either, continue trying to build a c file that you can use.

PaulS:
Correct.

But, why would you want to go to the hassle of creating a c file to be used in a cpp file, when adding two letters to the file name makes the whole problem go away?

Of course, you wouldn't. I said it was possible, not that it was a good idea or should be done. That's why I originally suggested renaming his C file to cpp.

Because they do things that do not require classes or the use of instances of classes. If you don't either, continue trying to build a c file that you can use.

Why? To save two letters when typing the name? Or is there a better reasong to continue calling it *.c instead of *.cpp. After all, with a .c extension, his code wouldn't compile. With a .cpp extension, it does compile. The easiest fix is to simply name it *.cpp. Or am I missing something? I know that you have more experience in these areas than me.

Or am I missing something?

There is no reason that I can think of today to create a new c file for use with a cpp/ino file. But, a lot of the code that drives the Arduino was written years ago, before C++ became the de facto standard. strlen(), strcmp(), strtok(), etc. are c functions, delivered in a .c file.

Historically, there were good reasons for choosing to develop code that could be used in a c file as well as in a cpp file. For other platforms, some of those reasons still hold. For the Arduino, where the main program is a cpp file, they do not.

Some people just like doing things the hard way, though. So, I pointed out that it was possible (I never even hinted that it was smart) to create a c file to be used with the cpp/ino file, as long as the developer does not try to use any C++ code in the c file.

PaulS:
There is no reason that I can think of today to create a new c file for use with a cpp/ino file. But, a lot of the code that drives the Arduino was written years ago, before C++ became the de facto standard. strlen(), strcmp(), strtok(), etc. are c functions, delivered in a .c file.

Historically, there were good reasons for choosing to develop code that could be used in a c file as well as in a cpp file. For other platforms, some of those reasons still hold. For the Arduino, where the main program is a cpp file, they do not.

Some people just like doing things the hard way, though. So, I pointed out that it was possible (I never even hinted that it was smart) to create a c file to be used with the cpp/ino file, as long as the developer does not try to use any C++ code in the c file.

Good one. Thanks for clarifying that Paul. I've always written in C++, since Visual Studio from about 15 years ago, and was hoping there were no reasons to revert to C at times. (I never did learn the finer points of C.)

Thank you gentlemen.
I was under impression that C is the (under the hood) "default" language. Well, looks like i was wrong.

The follow up question-
Arent 8kb of memory is a bit tight for all of the overhead that comes with oop?
What are the reasons to move away from C?

AZZ:
Thank you gentlemen.
I was under impression that C is the (under the hood) "default" language. Well, looks like i was wrong.

The follow up question-
Arent 8kb of memory is a bit tight for all of the overhead that comes with oop?
What are the reasons to move away from C?

C is antiquated, for one.
And object-oriented programming is much easier to implement for newcomers to programming -> Serial, String, SoftwareSerial, Servo, etc. The list goes on......
All of the hard work is done in the background.

And what 8K are you referring to?

OldSteve:
Of course, you wouldn't. I said it was possible, not that it was a good idea or should be done. That's why I originally suggested renaming his C file to cpp.

@OldSteve, I see that you sometimes think that comments to the OP are directed at you. It's a good policy to be specific with the @username, but we are usually not that pedantic here.

OldSteve:
... And what 8K are you referring to?

16 is seemed to be the minimum amount of memory . My bad. Yet still, isn't it a bit too low?

OldSteve:
... C is antiquated, for one.

"I'm old, but not obsolete"@Arny. I think kernel developers will be having a lot of arguments here.
I don't want to start the battle though. It's just seemed to be strange that all mighty CPP is used on on such a tiny processor. Yet, i'm by no means a cpp/c programmer, plus i never liked cpp so i can be very much biased and not exercising my good judgement here.

Here is in interesting discussion on C vs C++ for emb systems (http://electronics.stackexchange.com/questions/3027/is-c-suitable-for-embedded-systems)

aarg:
@OldSteve, I see that you sometimes think that comments to the OP are directed at you. It's a good policy to be specific with the @username, but we are usually not that pedantic here.

True, when they come straight after one of my replies. Maybe I'm too thin-skinned. :slight_smile:
@ is what I'm used to on other forums. When it's not used, a reply is often taken to be a reply to the previous post.
Here the "@" has little meaning, though, because it doesn't actually 'tag' a member.
I'll get the feel of things eventually. (I did say I'm a bit slow sometimes. :slight_smile: )

AZZ:
16 is seemed to be the minimum amount of memory . My bad. Yet still, isn't it a bit too low?

If there is only 16K of program memory, it's up to the user to avoid over-use of the C++ classes, I guess.
C++ doesn't automatically use more than C. Compile a blank *.ino file, with just empty setup() and loop() functions, and it uses virtually no memory. It depends on what you add.
Even adding serial doesn't use that much. An empty file like I mentioned compiles to 450 bytes.

Adding the following brings it up to 1802 bytes:-

    Serial.begin(9600);
    Serial.println("How much memory?");

Then the following brings it up to 1896 bytes:-

    Serial.begin(9600);
    Serial.println("How much memory?");
    Serial.println("qqqqqqqqqqqqwwwwwwwwwwwwwwwwwweeeeeeeeeeeerrrrrrrrrtttttttttttttyyyyyyyyyyyyyyyy");

Doing it without classes wouldn't take much less space, I reckon.

If the Arduino isn't big enough for the code, it's better to go up in Arduino size rather than complicate things by having only C, in my opinion.

I'm used to using tiny little 1K to 8K PIC chips for a lot of stuff, so the 32K of my UNO seems huge by comparison.

yeah, i'm on the other side of the spectrum where 120 GB of memory requires an upgrade. I still remember the ZX Spectrum (Z80 and 48KB of RAM) as a fantastic little computer, where builtin Basic was a tough task to run. But ASM was flying.... I guess that's what makes me uneasy about CPP .
All right i guess im looking for a cpp book than...

Thank you gentlemen.

AZZ:
yeah, i'm on the other side of the spectrum where 120 GB of memory requires an upgrade. I still remember the ZX Spectrum (Z80 and 48KB of RAM) as a fantastic little computer, where builtin Basic was a tough task to run. But ASM was flying.... I guess that's what makes me uneasy about CPP .
All right i guess im looking for a cpp book than...

Thank you gentlemen.

48K is was HUGE. My first computer was a Tandy TRS-80 - 4K of RAM. :frowning:
It too used the Z80 processor.

AZZ:
Arent 8kb of memory is a bit tight for all of the overhead that comes with oop?

First answer: yes. That's why people keep being told over and over "don't use the string class"
Second answer: there isn't that much overhead unless you are allocating on the heap using new.

C++ has some nice features - encapsulation/inheritance/polymorphism - that C doesn't. In the arduino context I suppose they wouldn't be used a lot, but the can be. I use them in my debounce library.

OldSteve:
Why? To save two letters when typing the name? Or is there a better reasong to continue calling it *.c instead of *.cpp. After all, with a .c extension, his code wouldn't compile. With a .cpp extension, it does compile. The easiest fix is to simply name it *.cpp. Or am I missing something? I know that you have more experience in these areas than me.

Some of us who have been programming in 'C' for a LONG time have built up a pretty good collection of reusable code that has been extensively tested and is generic enough to compile on many platforms. It makes sense to reuse existing code instead of having to modify it for some new platform if at all possible. Many of the platforms that I've worked on over the years did not even have a C-89 compiler, much less a C++ one. We would write our code in strict K&R 'C' since that was supported by all the compilers on the various platforms. Although C++ has some nice features, I can still create "objects" in standard 'C' -- it just takes a bit more discipline. Back when I was working for NASA (via one of the aerospace contractors) on the MCC and SSCC projects, the technique that we developed for creating reusable "objects" was to define datatypes with a typedef and struct give it a name that was used in all the "member functions" of the "object". The "object" was just a pointer to that structure, so basically an instance handle. For example, let's assume you want to create an "object" called "MYDATA". Here's how we would do it:

MYDATA.h

#ifndef _MYDATA_H_
#define _MYDATA_H_

    typedef struct mydata {
        int  x;
        int  y;
    } *MYDATA;

    int  MYDATA_Create( MYDATA *handlePtr );
    int  MYDATA_Close( MYDATA *handlePtr);
    int  MYDATA_MemberFn( MYDATA handle, int p1, int p2 );

#endif

MYDATA.c

#include "MYDATA.h"

int  MYDATA_Create( MYDATA *handlePtr )
{
    MYDATA  handle;

    handle = calloc(sizeof(struct my_data), 1);
    if (handle == NULL)
        return (-1);

    *handlePtr = handle;

    return (0);
}

int  MYDATA_Close( MYDATA *handlePtr )
{
    if ((handlePtr == NULL) || (*handlePtr == NULL))
        return (-1);

    free(*handlePtr);
    *handlePtr = NULL;

    return (0);
}

int  MYDATA_MemberFn( MY_DATA handle, int p1, int p2 )
{
    if (handle == NULL)
        return (-1);

    handle->x = p1;
    handle->y = p2;

    return (0);
}

And then a program that was going to use this "object" might look like this:

test-mydata.c

#include "MYDATA.h"

int  main( int argc, char **argv )
{
    MYDATA  obj;
    int     result;

    result = MYDATA_Create(&obj);
    if (result != 0) {
        printf("ERROR -- MYDATA_Create\n");
        exit (1);
    }

    result = MYDATA_MemberFn(obj, 10, 20);
    if (result != 0) {
        printf("ERROR -- MYDATA_MemberFn\n");
        exit (1);
    }

    result = MYDATA_Close(&obj);
    if (result != 0) {
        printf("ERROR -- MYDATA_Close\n");
        exit (1);
    }
}

I have not had very good luck in the Arduino IDE with being able to reuse my code that was written this way. It seems to work if you just have the main ".ino" file with everything in it, but if you separate each "object" into a separate header ".h" and source ".c" file, you start getting compilation errors. And it really doesn't seem to like it when you try to put these "object" files in other directories.

http://www.monkeywrench.space/navy-vet-1959/objects-in-c.htm

Yeah, yeah (you're also responding to a 5-year old post.)

And if you simply renamed your existing .c files to .cpp files, you'd have a much easier time getting them to work, and/or created .cpp files containing C code, because you'd no longer have to deal with the C vs C++ "name mangling."
Most C code is still legal C++ code...

Of course, anything based on dynamic allocation (calloc()) probably won't work very well on a microcontroller with only 2k of RAM...