Sign in to follow this  
Ezbez

Switching from C++ to C: any suggestions?

Recommended Posts

I've just joined my high school's FIRST robotics team, and I'm in the programming group. We *have* to use C, not C++, because, well, I'm not 100% sure, but it's either that there's no C++ compiler for this processor, or that the rules require us to for some reason. Suffice it to say that this is out of my control. So, is there anything I should keep in mind comming from an almost entirely C++ background? I know about the obvious differences, like not having templates, classes, having to declare variables at the start of the scope, and much of the standard library, but I don't know much more than that. Any common mistakes or such for a C++ programmer to make in C? Thanks in advanced.

Share this post


Link to post
Share on other sites
Well, I've done it before. The biggest mistake is to try and emulate C++ things in C. You have to fully adapt. Apart from that, there's not a lot else, just be a lot more careful with your dynamic memory allocations.

Share this post


Link to post
Share on other sites
about every second line should include an assertion. assert everything!!!111
working with raw pointers and especially void-pointers all the time is pretty much the biggest source of errors in C, so do absolutely everything to find errors there as soon as possible, otherwise complete frustration is guaranteed.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ezbez
I've just joined my high school's FIRST robotics team, and I'm in the programming group. We *have* to use C, not C++, because, well, I'm not 100% sure, but it's either that there's no C++ compiler for this processor, or that the rules require us to for some reason. Suffice it to say that this is out of my control.

So, is there anything I should keep in mind comming from an almost entirely C++ background? I know about the obvious differences, like not having templates, classes, having to declare variables at the start of the scope, and much of the standard library, but I don't know much more than that. Any common mistakes or such for a C++ programmer to make in C?

Thanks in advanced.


C is just easier than C++. There are some difference in the standard library, struct are no longer classes (because there is no classes) and can no longer be defined using the C++ syntax - a typical usage is to typedef an unnamed struct to a type name:
typedef struct
{
int field1;
int field2;
} MyStructName;

However, there are some other strange things this notation: since you didn't defined the type yet, you can't use it in the structure itself.
typedef struct
{
ListElem *next;
ListElem *prev;
} ListElem;

Is invalid. It have to be either

typedef struct list_elem_t ListElem;
struct list_elem_t
{
ListElem *next;
ListElem *prev;
};

Or
typedef struct list_elem_t
{
struct list_elem_t *next;
struct list_elem_t *prev;
} ListElem;

You must typedef enum too (if you plan to use MyEnum instead of "enum my_enum") This is the biggest syntax difference I know of.

The C++ draft here (pdf) has a Compatibility annex with some of the most notable differences between C and C++. It is worth a look.

HTH,

Share this post


Link to post
Share on other sites
Quote:
We *have* to use C, not C++, because, well, I'm not 100% sure, but it's either that there's no C++ compiler for this processor, or that the rules require us to for some reason. Suffice it to say that this is out of my control.


I don't know what kind of hardware you're using but lots of embedded platforms only have c compilers available. Which is probably the reason you have to use C.

You shouldn't have too many problems, I doubt you're going to be writing all that much code you'll spend more time on coming up with good algorithms getting the hardware working, sorting out mechanics etc.

Share this post


Link to post
Share on other sites
If you're dealing with a processor that doesn't have a C++ compiler, then odds are it's C compiler isn't that great either so don't rely on behaviour in C99.

* Watch you're casting!! C is far, far too forgiving when it comes to type checking.

* There's no reason you can't do object-oriented programming in plain old C. You can even do inheritence and polymorphism.

typedef struct
{
void (*destroy)(Object*);
bool (*update)(Object*, int);
} Object;

void destroyObject(Object* obj)
{
obj->destroy(obj);
free(obj);
}

void updateObject(Object* obj)
{
obj->update(obj);
}

typedef struct
{
Object obj;
Vertex *vertices;
} Model;

Object* createModel(int numVerts)
{
Model *this = malloc(sizeof(Model));
this->obj.destroy = &Model_destroy;
this->obj.update = &Model_update;
this->vertices = malloc(sizeof(Vertex)*numVerts);
return &this->obj;
}

void Model_destroy(Model* this)
{
free(this->vertices);
}

void Model_update(Model* update)
{
// Do stuff with vertices
}

void doStuff()
{
Object *obj;
obj = Model_create(10);
updateObject(obj);
destroyObject(obj);
}



* Make sure you've got function prototypes, because older C compilers won't even warn you about not providing them. They'll gladly let you do the following:

int func1()
{
return func2(10);
}

int func2(int a, int b)
{
return a + b;
}


* Check if you're processor is 16-bit or 32-bit (or *shudder*...8-bit!). If it's not 32-bit then you'll have to keep an eye on your integer arithmetic because an 'int' will only be 16-bit (longs are usually still 32-bit though but no guarenttees, check your compiler documentation).

* Your CPU probably doesn't have floating-point support (although it might be emulated)

Share this post


Link to post
Share on other sites
Naming things is a bit of a pain in C as well, due to the lack of namespaces etc. I've found when I've been forced to use C, I need to split things up into a lot more translation units than C++ in order to hide implementation details and so on.

You can also end up relying on the preprocessor a lot more which is possibly the most common cause of hard-to-track bugs and errors.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
for any non-trivial project, it might in fact be more feasible to simply port the processor/architecture to gcc, so that it is supported as a new backend by the compiler, this will usually provide with language support for ALL gcc frontends (c, c++, ada, fortran, java etc). Also, adding new backends to gcc has meanwhile become a relatively straight forward thing to do, there's good documentation available for most architectures, porting comes down to copying/pasting the corresponding passages from other targets. gcc has in fact become a pretty viable compiler framework to be used even for embedded platforms, there are for example ATMEL targets and even PIC targets (or patches) available for gcc.

And even if you should decide not to use gcc and to definitely use a corresponding embedded C compiler, you may still want to consider using C++ as the programming language, if you check out the gcc-patches mailing list, you'll find a patch that allows arbitrary frontend code translated from RTL to C, that way you can have anything converted to (UGLY) C, but still C.

However, this may only be feasible if you have to manage a truly non-trivial project, which C is certainly not the most ideal language for, which is also heavily illustrated by the gcc project itself.

Share this post


Link to post
Share on other sites
- Learn about function pointers.
- Learn about the volatile keyword. If you're writing C to talk to hardware, you will inevitably be reading data from pins and sensors, meaning you don't want it to be cached - everytime you read it, it has to be the correct, current value. I'm sure they'll drill you about this on your first day anyway.

Have fun!

Share this post


Link to post
Share on other sites
The main difference for me was that you have to pass a pointer to your data structs instead of just using a class.

For example in C++:


class MyClass
{
public:
MyClass( int a ) : m_MyData(a) { }

void DoSomething();
int DoSomethingElse( int a );

int m_MyData;
}

//
MyClass *c = new MyClass( 5 );
c->DoSomething();
int r = c->DoSomethingElse( 10 );
delete c;


You have something such as:-


struct MyData
{
int MyData;
};

MyData *MyDataCreate()
{
MyData *r = (MyData*)malloc( sizeof(MyData) );
}

void MyDataDestroy( MyData *a_data )
{
free( a_data );
}

void MyDataDoSomething( MyData *a_data )
{
// Blah
}

int MyDataDoSomethingElse( MyData *a_data, int a )
{
a_data->MyData += a;
return a_data->MyData;
}


//
MyData *d = MyDataCreate();
MyDataDoSomething( d );
int r = MyDataDoSomethingElse( d, 10 );
MyDataDestroy(d);


I actually found it easier to encapulate systems in C than in C++ - even with the Object-oriented paradigm but that's maybe because I just suck at C++ sometimes ;) I find that I'm better able to define my interfaces when using C, I don't know why - I think the act of keeping everything in separate header files and distinguishing them as public/private seems more intuitive than having a class.

Share this post


Link to post
Share on other sites
The biggest bugaboos in C that I no longer encounter when using C++ include the following (and may not apply to your particular applications):

(1) strcmp() returns false for true (ie. 0, which is akin to false, means the strings are equal).

(2) [fs]scanf() takes a _pointer_ to the destination variables.

(3) forgetting to make strings big enough to hold the terminating null.

(4) sign extension on implicit char-to-int conversions (eg. char c=0xba; assert(c==0xba); will assert on some platforms).

(5) See 3. Repeated for emphasis.

Those above items continued to bite my behind years after I mastered the intricacies of the language and had decades of professsional C coding behind me.

Share this post


Link to post
Share on other sites
Quote:
Original post by joanusdmentia
[...]* There's no reason you can't do object-oriented programming in plain old C. You can even do inheritence and polymorphism. [Source Removed][...]
Or you could use virtual tables:
typedef struct s_Object Object;
typedef struct
{
void (*Destroy)(Object*);
bool (*Update)(Object*, int);
} Object_VTable;

typedef struct s_Object
{
Object_VTable *VTable;
double X, Y;
} Object;

/*...*/

typedef struct
{
Object SuperClass;
/*...*/
} Model;

Object_VTable Model_Object_VTable = {Model_Destroy, Model_Update};

/*...*/

Model M;
M.SuperClass.VTable = &Model_Object_VTable;




With VTables, you don't need for each object to store a function pointer for every function, only to store a pointer to the table of functions for that type. The cost is that it takes an extra indirection, which might or might not be significant on the embedded platform (depending on the timing requirements and the complexity of the system)

You might want to look at COM headers to see how MS handles that kind of thing (porting polymorphic C++ interfaces to C)

Share this post


Link to post
Share on other sites
There exists C++ compilers that compile to ANSI C. So if you really want to write in C++, you can.

You may want to examine the specs of the hardware, however.

Share this post


Link to post
Share on other sites
Some of the biggest announces are going to be string related. Stream operators (<< and >> ) dont exist, and obviously streams themselves no longer exist, so get to know printf/scanf and fopen well.

bool isnt a keyword. This one can lead to some nastiness back porting code.

Obviously new/delete dont exist, so your back to malloc() and free().

Casting is less safe... or more flexable... depending if you are a glass half empty/full kinda guy :) But no dynamic or static_cast in C land.

A really minor one, that annoys the hell out of me... no single line comments

// So this comment
needs to be
/*
This comment
*/

On the topic of comments, nesting of comments is a no no, which is also annoying.

/* So this /* is illegal */ in C */

I *believe* there is no such concept of pubilc or private in C. I dont remember 100%, but I think all structs are basically public. Don't quote me on this one.


And finally, and this one burns me the most if I ever do C code. As you know, all variables need to be pre-declared. That includes in loops, so no more:

for(int i =0; i < 42; i++)

That one I seem to always screw up.

Share this post


Link to post
Share on other sites
Considering all the other problems you're going to be facing, I recommend you ignore all the people suggesting you program using objects and inheritance and stuff. Unless you've got experience in robotics and embedded development, that's just going to be one more headache you don't need. KISS is your friend.

Really, it isn't so much "how do I switch from C++ to C", but "How doI switch from computer programming to microcontroller programming?" You're going to make extensive use of global variables, for instance, its just unavoidable. You have to keep extremely close watch on how much memory you use...assuming malloc is available, it shouldn't be used willy nilly. Things like interupts are going to force you to watch for concurrency. Macros should be prefered to functions for simple operations. And so forth.

But when it comes right down to it, I suggest you just get to coding. Get your board setup sufficiently that you can figure out how to interact with everything, and just have fun. You'll figure out the important bits as you go.

*edit: BTW, /* this /* is also illegal */ in C++ */

CM

Share this post


Link to post
Share on other sites
Quote:
(1) strcmp() returns false for true (ie. 0, which is akin to false, means the strings are equal).

im pretty sure stl::string compare does as well

Share this post


Link to post
Share on other sites
Quote:
Original post by Conner McCloud

*edit: BTW, /* this /* is also illegal */ in C++ */

CM


Oops.

Well... I was still right... it is illegal in C! [smile]

I havent used a block comment in years, outside of C. Got foggy on that one.

Share this post


Link to post
Share on other sites
I wrote some stuff in C recently.

I wrote it in C++ using just C features. Then I switched my compiler to C mode and fixed the syntax errors. It was easy.

Share this post


Link to post
Share on other sites
Thanks all! Sounds like lots of the common mistakes are string-based, and I doubt I'll be doing much of that.

Share this post


Link to post
Share on other sites
Quote:
Original post by zedzeek
Quote:
(1) strcmp() returns false for true (ie. 0, which is akin to false, means the strings are equal).

im pretty sure std::string compare does as well


Yes (it has to, because it's a "3-way lexicographical compare"), but you almost never use compare() directly in C++ anyway.

Share this post


Link to post
Share on other sites
Quote:
Original post by Serapth
I *believe* there is no such concept of pubilc or private in C. I dont remember 100%, but I think all structs are basically public. Don't quote me on this one.


Well, considering there's no member functions, I'm not sure what code would have access to a supposed private struct member in C :)

Also, someone above casted the return value of malloc. C supports implicit casting from void* to any other T*, so don't cast malloc.

Share this post


Link to post
Share on other sites
One thing that will probably help a lot is to be absolutely anal about const correctness!

Honestly I see alot of code, both C and C++, which does not demonstrate proper const correctness. Not only does it help the compiler optimize your application (particularly on the older or embedded-target compilers you'll likely be dealing with) but it will prevent you from introducing inadvertant errors because the compiler will bitch if you have a lapse in judgement and try to change a const variable. This is especially true for pointers -- const pointers to mutable objects, const pointers to const objects, mutable pointers to const objects. I use the word 'objects' here to mean variables, structures, and function pointers... Any addressable storage, for that matter.

I'm not saying that everything should be const because, obviously, some things will need to be mutable (non-const.) What I'm saying is simply that everything which does not [i]need[i] to be mutable should be const. To me, and more importantly, to the compiler, the lack of a const specifier says that this variable is free to be changed, even if it does not make semantic sense that it would be.

Take for example the following function:

void add(vec3 *result, vec3 *lhs, vec3 *rhs)
{
result->x = lhs->x + rhs->x;
result->y = lhs->y + rhs->y;
result->z = lhs->z + rhs->z;
}


This is a perfectly valid function, but there's no promise that lhs nor rhs will not change. You could do this, for instance:

void add(vec3 *result, vec3 *lhs, vec3 *rhs)
{
result->x = lhs->x += rhs->x;
result->y = lhs->y += rhs->y;
result->z = lhs->z += rhs->z;
}


The result is correct, but we've mistakenly changed one of the input vectors to also contain the result. Worse yet, we could do something as bad as:

void add(vec3 *result, vec3 *lhs, vec3 *rhs)
{
result += rand() % 512;
rhs = (vec3*)0xDEADBEEF;
result->x = lhs->x += rhs->x;
result->y = lhs->y += rhs->y;
result->z = lhs->z += rhs->z;
lhs = (vec3 *)0x00000000;
}


Now, we've got incorrect results which are stored God knows where, followed by an impending crash next time we access whatever vec3 lhs is pointing at... assuming that the process owns the memory at 0xDEADBEEF, and the next 512 bytes after &result, as to not bring the program down with the first call to add().

Here is what a much safer add function would look like:

void add(vec3 * const result, const vec3* const lhs, const vec3* const rhs)
{
result->x = lhs->x + rhs->x;
result->y = lhs->y + rhs->y;
result->z = lhs->z + rhs->z;
}


This function prevents us from A) modifying any of the pointers to point to anywhere they shouldn't, and B) from changing the contents of the source parameters.

This will take a little getting used to, I admit the syntax is ugly and even difficult to read at first, though you can use a macro (BE CAREFULL!) to hide the ugliness if you wish:

#define C_REF(type) type * const

void add(C_REF(vec3) result, const C_REF(vec3) lhs, const C_REF(vec3) rhs)
{
result->x = lhs->x + rhs->x;
result->y = lhs->y + rhs->y;
result->z = lhs->z + rhs->z;
}




Another good suggestion is to be careful with your memory management. Try not to sprinkle malloc/calloc/free throughout your program willy-nilly, but rather at defined places, factory functions are good candidates (and don't forget to error-check):

Vec3* CreateVec3(int x, int y, int z)
{
Vec3* v = malloc(sizeof(Vec3));

if(v != 0)
{
v->x = x;
v->y = y;
v->z = z;
}

return vec;
}


It would also be great to write a logging malloc/calloc wrapper to keep track ofmemory allocations/deallocations to help detect and track down memory leaks, though since you're on an embedded platform you'll probably want to simply log allocation/deallocation (to a file or serial port) using the address, size, file and line of the allocation and address of the deallocation. Then create an external tool to compare allocations to deallocations do identify the magnitude and location of memory leaks.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this