Moving from C ++ to C

After several years of programming in C ++, I was recently offered the job encoding in C in a built-in field.

Putting aside the question of whether it is right or wrong to reject C ++ in the built-in field, there are some features / idioms in C ++ that I would miss a lot. Just to name a few:

  • General, strict data structures (using templates).
  • RAII. Especially in functions with multiple return points, for example. remember to release the mutex at every return point.
  • Destructors in general. That is, you write d'tor once for MyClass, then if the MyClass instance is a member of MyOtherClass, MyOtherClass should not explicitly deinitialize the MyClass instance - its call is called automatically.
  • Namespaces.

What is your experience moving from C ++ to C?
What C substitutes have you found for your favorite C ++ / idiom functions? Have you discovered any C functions that you would like to have in C ++?

+79
c ++ c
30 Oct '10 at 11:35
source share
8 answers

While working on an embedded project, I tried to work in all C once and just could not bear it. It was so much that it was hard to read anything. In addition, I liked the optimized for built-in containers that I wrote, which should have become less secure and difficult to fix #define blocks.

The code in C ++ looked like this:

 if(uart[0]->Send(pktQueue.Top(), sizeof(Packet))) pktQueue.Dequeue(1); 

turns into:

 if(UART_uchar_SendBlock(uart[0], Queue_Packet_Top(pktQueue), sizeof(Packet))) Queue_Packet_Dequeue(pktQueue, 1); 

which many are likely to say is fine, but becomes ridiculous if you need to do more than a couple of "methods" of calls in a line. Two C ++ strings turned into five from C (due to 80-char string length restrictions). Both will generate the same code, so it doesn't look like the target processor!

Once (back in 1995) I tried to write a lot of C for a multiprocessor data processing program. A type in which each processor has its own memory and program. The compiler supplied by the vendor was a C compiler (some kind of HighC derivative), their libraries were private, so I could not use GCC to build, and their APIs were designed taking into account that your programs will be initialized / processed first / stop diversity, so interprocessor communication was rudimentary at best.

I had about a month before I gave up, found a copy of cfront and hacked it into make files, so I can use C ++. Cfront did not even support templates, but C ++ code was much clearer.

General, robust data structures (using templates).

The closest thing to C templates is to declare a header file with lots of code that looks like this:

 TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this) { /* ... */ } 

then pull it in with something like:

 #define TYPE Packet #include "Queue.h" #undef TYPE 

Note that this will not work for composite types (for example, without unsigned char queues) unless you make a typedef .

Oh, and remember that if this code is actually not used anywhere, then you don’t even know if it is syntactically correct.

EDIT: One more thing: you need to manually manage code generation. If your "boilerplate" code does not have all the built-in functions, then you will need to install some control to make sure that all objects are created only once, so your linker has not spat out a bunch of "several instances of Foo errors."

To do this, you will need to place the non-embedded material in the "implementation" section of the header file:

 #ifdef implementation_##TYPE /* Non-inlines, "static members", global definitions, etc. go here. */ #endif 

And then, in one place in all of your code for each template variant, you should:

 #define TYPE Packet #define implementation_Packet #include "Queue.h" #undef TYPE 

In addition, this implementation section should be outside the standard #ifndef / #define / #endif litany, because you can include the template header file in another header file, but then you need to create an instance in the .c file.

Yes, it gets ugly. This is why most C programmers don't even try.

RAII.

Especially in functions with multiple return points, for example. remember to release the mutex at every return point.

Ok, forget your beautiful code and get used to all your return points (except the end of the function) goto s:

 TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this) { TYPE * result; Mutex_Lock(this->lock); if(this->head == this->tail) { result = 0; goto Queue_##TYPE##_Top_exit:; } /* Figure out `result` for real, then fall through to... */ Queue_##TYPE##_Top_exit: Mutex_Lock(this->lock); return result; } 

Destructors in general.

those. you write d'tor once for MyClass, then if the MyClass instance is a member of MyOtherClass, MyOtherClass should not explicitly deinitialize the MyClass instance - its call is called automatically.

The design of the object must be explicitly handled the same way.

namespaces.

This is actually a simple task: just attach a prefix to each character. This is the main reason for inflating the source I talked about earlier (since classes are implicit namespaces). People C lived this, well, forever, and probably won’t see what a big deal is.

Ymmv

+64
30 Oct. '10 at 12:22
source share

I switched from C ++ to C for another reason (some kind of allergic reaction), and there are only a few things that I miss and some things that I have acquired. If you stick to C99, if possible, there are constructs that allow you to program quite well and safely, in particular

  • designated initializers (eventually combined with macros) make initialization of simple classes as painless as constructors
  • compound literals for temporary variables
  • for -scope can help you do resource-based management with areas , in particular, unlock mutexes or free arrays, even with a preliminary function returns
  • __VA_ARGS__ macros can be used to use default arguments for functions and to deploy code
  • inline functions and macros that combine well to replace (sort) overloaded functions
+17
Oct. 30 '10 at 12:50
source share

Nothing like STL for C.
There are available libraries that provide similar functionality, but they are no longer built-in.

Think that this is one of my biggest problems ... Knowing which tool I could solve the problem, but not having the tools available in the language that I should use.

+8
Oct 30 2018-10-10
source share

The difference between C and C ++ is the predictability of code behavior.

It’s easier to predict with great accuracy what your code will do in C, in C ++ it may be a little harder to come up with an accurate prediction.

Predictability in C gives you better control over what your code does, but it also means you need to do more things.

In C ++ you can write less code in order to do the same, but (while for me). I sometimes have problems understanding how the object code is laid out in memory and the expected behavior.

+8
30 Oct '10 at 17:30
source share

In my work, which, incidentally, is built-in, I constantly switch between C and C ++.

When I am in C, I will skip from C ++:

  • (including, but not limited to, STL containers). I use them for things like special counters, buffer pools, etc. (Created my own library of class templates and function templates, which I use in various embedded projects)

  • very powerful standard library

  • which, of course, make RAII possible (mutexes, interruptions, tracking, etc.)

  • to better ensure who can use (not see) that

I use inheritance for larger projects, and the built-in C ++ support is much cleaner and more pleasant than C to “crack” the base class attachment as the first member (not to mention the automatic call of constructors, initialization lists, etc.), but The points listed above are the ones that I miss the most.

Also, perhaps only about a third of the C ++ embedded projects I work on use exceptions, so I'm used to living without them, so I don’t miss them too much when I return to C.

On the other hand, when I return to project C with a significant number of developers, there are whole classes of problems in C ++ that I use to explain to people who leave. Mostly the problems are due to the complexity of C ++ and people who think they know what is happening, but they really are in the “C with classes” section of the C ++ Trust Curve .

Given this choice, I would prefer to use C ++ for the project, but only if the team is pretty solid in the language. Also, of course, assuming this is not an 8K μC project, where I generally write "C".

+7
Oct 30 '10 at 16:16
source share

Pair of observations

  • If you do not plan to use your C ++ compiler to create your C (which is possible if you stick to a clear subset of C ++), you will soon find that your compiler will allow in C, which will be a compilation error in C ++.
  • No more bugs in critical patterns (yay!)
  • No (language supported) object-oriented programming
+3
Oct 30 '10 at 11:41
source share

For the most part, the same reasons as for using C ++ or a combination of C / C ++ rather than pure C. I can live without namespaces, but I use them all the time if the code standard allows it. The reasons for this are that you can write much more compact C ++ code. This is very useful for me, I write servers in C ++, which tend to crash from time to time. At this point, it helps a lot if the code you are looking at is short and composed. For example, consider the following code:

 uint32_t ScoreList::FindHighScore( uint32_t p_PlayerId) { MutexLock lock(m_Lock); uint32_t highScore = 0; for(int i = 0; i < m_Players.Size(); i++) { Player& player = m_Players[i]; if(player.m_Score > highScore) highScore = player.m_Score; } return highScore; } 

In C, which looks like this:

 uint32_t ScoreList_getHighScore( ScoreList* p_ScoreList) { uint32_t highScore = 0; Mutex_Lock(p_ScoreList->m_Lock); for(int i = 0; i < Array_GetSize(p_ScoreList->m_Players); i++) { Player* player = p_ScoreList->m_Players[i]; if(player->m_Score > highScore) highScore = player->m_Score; } Mutex_UnLock(p_ScoreList->m_Lock); return highScore; } 

Not a world of difference. Another line of code, but it tends to add up. Nomally you try to keep it clean and drown, but sometimes you need to do something more complicated. And in such situations, you value the number of lines. Another line is another thing you need to pay attention to when you are trying to understand why your broadcast network suddenly stops delivering messages.

In any case, I believe that C ++ allows me to do more complex things in a safe way.

+2
30 Oct '10 at 17:03
source share

I think the main problem why C ++ is harder to accept in the embedded environment is the lack of engineers who understand how to use C ++ correctly.

Yes, the same reasoning can be applied to C, but, fortunately, there are not many pitfalls in C that can shoot in the foot. C ++, on the other hand, you need to know when not to use certain functions in C ++.

All in all, I like C ++. I use this at the O / S service level, driver, control code, etc. But if your team lacks experience, this will be a daunting task.

I had experience with both. When the rest of the team was not ready for this, it was a complete disaster. On the other hand, it was a good experience.

0
Oct 31 '10 at 19:48
source share



All Articles