C and C++ - not a war

Go To Last Post
49 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've been inspired by the following thread:
https://www.avrfreaks.net/index.p...

Likewise, I'm not interested in "C++ has code bloat than C" arguments any more than "C has more code bloat than asm" etc.

There does, however, appear to be a massive disconnect between the practitioners of the two languages, starting at Linus Torvalds down [1].

I'm wondering, what barriers are people encountering before switching from one to another, even on the "host" side of the code, where resources generally aren't an issue.

Like Johan's thread, I'm not interested in starting a war - I'm wondering what disconnect there is and what we can do for mutual understanding.

-- Damien

[1] http://thread.gmane.org/gmane.co...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is there a good article, which describe the advantages of C++ from the practical view?

I have only seen theoretical articles (database applications) and found nothing to be reusable on microcontroller applications.

If I look on C++ code, I see only tons of headers and lost fully the overview. :cry:

C++ sources are typically many times larger than C sources (more writing needed).

Thus until now, I found no entry point to start with C++ and understand C++.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh... I was thinking of this too.

As most residents know I have been a vivid participant in the C/C++ wars that we are not to have now. If you want to do that thing again I'm your Huckleberry, but let's take it to it's own thread then so that we don't ruin this excellent attempt to discuss a genuinely interesting subject.

I have a lot more to go on re the C -> C++ transition than the asm -> C transition.

For C -> C++ it is not at all about things like
- understanding the functioning of the tool chain
- basic concept of "type"
etc

It is the "thinking in objects" ability that is the culprit. I have, on a daily basis, been working with code in an OO language (be it C++, C# or Java) for several (uh, like in many, ie 10+) years. Most of it has been a lame attempt, more of a "hide C in C++" than actually making the design effort and then code. And make the design OO to begin with. I talk to a lot of C# programmers that has more or less missed the point. When I talk to them about how I would design for a specific case I regularly see everything from blank faces (those not getting it at all) to embarrassed surprise (those who suddenly get it, or even know from the start how it should be but just didn't bother).

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

It is the "thinking in objects" ability that is the culprit.

Oh I have no problems with "objects" and methods and have a pretty fair understanding of Java (and also the totally unrelated Javascript as well as ASP/VBscript) and a passing acquaintance with the Microsoft OO languages but I'm afraid I don't think I'm ever going to "get" C++

Not sure exactly what it is but the syntax seems to be too obscure to easily be able to get a mental picture of exactly what's going on (like I say I don't get this with Java).

Every time I ask someone to present some simple examples of traditional C side by side with the equivalent done in C++ nothing is ever shown - is it because the "gurus" want to keep it a "black art"? Is it too difficult to construct equivalent samples? (and if so, why? presumably this is the basis of the question in this thread?)

Perhaps the problem is lack of decent training info? What is the C++ equivalent of K&R for example? Or is there some other (easily readable!) "bible"?

BTW can anyone explain to me why:

#include 

main()
{
    cout << "Hello World!" << endl;
    return 0;
}

is a preferable syntax to:

#include 

main() {
    printf("hello, world\n");
}

This isn't to start the actual war but I simply have no idea why they changed it (to my mind for the worse!) and this is actually a stumbling block for C programmers switching from C to C++ - not in the emebedded environment but on the desktop which is the obvious place to start playing with C++. If I want to see "what's going on" then previously I'd be tempted to liberally sprinkle code with printf()s, now it's some tortuous syntax in which I woudn't know where to start if I want to include a %04d in there

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff wrote:
BTW can anyone explain to me why [streams] is a preferable syntax to [printf]
Whoever thought it up was used to the old BASIC "PRINT" statements? :lol: :twisted:

I have several C++ apps, both written by me and written by others. The only time I start seeing the [streams] syntax is when the original author comes from a "web" background. Since PERL/PHP uses this kind of syntax, it makes sense to allow it.

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
BTW can anyone explain to me why [streams] is a preferable syntax to [printf]

One argument is that a stream is an object, ie an instance of a class. So normal "OO thinking" allows you to take a stream class, inherit it into your specialization and override some of the methods/operators.

Without streams you'd have to write your own base class that uses printf, and then inherit from it. Apart from superficial stuff (like streams exposing their API dominantely through operators rather than through function calls) you now have invented your own stream class.

And I'm not even sure if I can use an object as a parameter to printf. Thinking a few seconds about it I'm almost sure I can't.

This question again is a display of how the idea of OO is bypassed. Let's say I have (sketchy):

SomeClass myObject = new SomeClass( ... );
myObject.someOperation();

stream theStream = new stream(...);

// Now!
theStream << myObject;

In the last line, the << operator is overloaded so that SomeClass "knows" how to represent itself textually for a stream. This might be well for eg some display on a screen. Now, assume for printing you want another representation. This would go

SomeClass myObject = new SomeClass( ... );
myObject.someOperation();

stream theStream = new printStream(...);

// Now!
theStream << myObject;

Notice what has changed? And more important, notice what has not changed? Get it?

Let's say you want to list all objects on some "collection", and by and large this should be done the same way regardless of if you are directing the listing to the screen or to a printer. Sooo.. take a deep breath now!

(For the sake of simplicity the collection and count of objects is very clumpsy. In the real world youd probably have some more elegant collection, and ways to iterate though it's items)

void ListObjects(MyClass objects[], int objectCount, stream theStream)
{
   for (int i = 0; i
and in handlers for the "ScreenList" and "Print" menu items you'd have (assuming variables for the object collection and the object count)
void handleScreenList()
{
   ListObjects(theObjectArray, theObjectArrayCount, new screenStream(...));
}
void handlePrintList()
{
   ListObjects(theObjectArray, theObjectArrayCount, new printStream(...));
}

--------------------------

The aspect that you can inherit a stream base class, and specialize it makes for a whole new world of possibilities. We are now moving into the realm of eg "Design Patterns". Some are esoteric. Some are really useful. For the interested reader the pattern "Decorator" is often used together with streams, eg in Java. This allows you to do eg translation of character coding, while retaining the full streams API, and it allows you to "stack" (decorate) a stream with several such additions to the basic functionality on top of each other. Used in the right circumstances it makes life a lot easier. Used wrongly it's a menace. Nothing new introduced with OO in that aspect...

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Perhaps the problem is lack of decent training info? What is the C++ equivalent of K&R for example? Or is there some other (easily readable!) "bible"?

Here is the K&R of C++
http://www2.research.att.com/~bs/3rd.html

For me there is no disconnect; C is a subset of C++. You should use tools from each part of the language necessary to solve a specific problem cleanly and efficiently.

Large scale game development would be complete mess without some of the features of C++.

I have too many hobbies.
s-conductor.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Johan,

Thanks for the explanation but I'm just sitting here goggle eyed and slowly dribbling from time to time. I'm afraid that 95% of that went straight over my head. I guess I just cannot see what was wrong with printf() and what problem this use of "<<" has solved for me. If I didn't know I had the problem or even what it was why would I go looking for a solution that wasn't printf()?

Put it this way what can you do with << that I simply cannot do with an invocation of (s)printf()?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Put it this way what can you do with << that I simply cannot do with an invocation of (s)printf()?
I'm not sure that you can.
Much as I like C++, I find the streams stuff clumsy.
What it gets you is type-checking and extensibility.
Once you code your own operator<<(std::ostream&, const MyType&),
you can use it just like the predefined versions.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Surely the main difference between C and C++ is not how you print stuff (printf vs cout <<). You can perfectly well use printf in C++ if you like.

I have never ever used C++ on microcontrollers, and coming from a Turbo Pascal hobby background, I knew how to read C but I never wrote C programs, the mandatory uni courses (CS minor) had C++ so I learnt C that way. To me, writing first AVR programs in C was a pain because I had to learn how printf works and most of the tricks available in C++ was not available in C. OK, in microcontroller world, the tricks don't have a real purpose.

MCU programs tend to be small so you can write modules or objects in C with just separate files, without making them as C++ classes. PC programs I write tend to do more stuff as easily as possible, so with C++ there are things that help like STL library that has strings and maps and vectors etc all written for me.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Thanks for the explanation but I'm just sitting here goggle eyed and slowly dribbling from time to time.

:D

Quote:

I'm afraid that 95% of that went straight over my head. I guess I just cannot see what was wrong with printf() and what problem this use of "<<" has solved for me.

Answer 1: Exercise: Assume that you have a struct type, and an array of such structs. The data in the struct is to be be printed to a report, but how each struct is displayed depends on if the output is going to a printer or if it is going to the screen. Sketch how to solve this in C in a solution using printf.

Answer 2: Then you are just not ready to be enlightened. No, not entirely a joke although formulated as such. OO is to a substantial part about being able to see the world in a different way. I'm sorry, but I kind of repeat that old stuff about if you naturally think

DriveCar(aCar);

or

aCar.Drive();

This gets "OO hot" when comparing (assuming there are structs / objects aCar and aBus) C

DriveCar(aCar);
DriveBus(aBus);

and C++

aCar.Drive();
aBus.Drive();

So far no big revolution. But suppose you have a variable referring to a vehicle (which was created earlier as either a Car or a Bus, but a this stage you dont care, you just want it to drive). In C this is hard(er) than in C++ where you'd simply

theVehicle.Drive();

which is just "plain old polymorphism" at work. (For this to work we'd have a superclass Vehicle that stipulates the interface to the class, eg that it must have a Drive() method. We'd then inherit those to the subclasses Car and Bus, which must implement their own versions of the method. (Leaving the details of how this is accomplished. If you want I'll sketch it up sometime in Easter).

Now lets get technical: How would you do the above in C? Well, assuming that cars and buses are variants in a union which in turn is part of a struct that starts with a field identifying what type of vehicle it is you could start like this (again sketchy):

// Drive a vehicle regardless of which type is actually is
if (theVehicle.type == CAR)
{
   DriveCar(theVehicle);  // This function uses the car variant of the union
}
else if (theVehicle.type == BUS)
{
   DriveBus(theVehicle);
}
etc...

Your next attempt is to embed a function pointer in the struct (outside of the union) and let it point to the driving function that is suitable for the specific type of vehicle that this struct represents. So now you can (even sketchier)

theVehicle->Drive(theVehicle);

After that you realize that if you have a lot of vehicles, and they all can do a lot of different things, a lot of space will be wasted on duplicated function pointers (eg all cars will have identical function pointers). So you break that out to a separate struct, which will only exist in one instance for each type of vehicle, and let each instance of a vehicle point to that shared struct. You now will pay the price of a double indirection

theVehicle->functionTable->Drive(theVehicle);

Congratulations! You have just invented C++. But all the setting up of the function table, pointers to them etc will have to be done explicitly. When you code a virtual function in C++, the compiler will do all this for you, and you are left with

theVehicle->Drive();

"But the parameter to the Drive function?", you ask. In C it is needed so that the Drive function can get the specifics of the vehicle in question (eg to get the number of horse-powers the vehicle engine gives). Yes, it is passed in C++ also, without you having to code explicitly for it. In every member function in a class you have an implicit pointer called this that you can use to get to the objects specific data.

As I have explained in earlier essay on the subject the double indirection is the price you pay for virtual functions, but only for virtual functions. Functions not "playing the polymorphic game" is resolved at link time just as in C and has the same cost in code size and execution.

On the philosophical level each class is responsible for defining it's behaviour. This is something quite different from free-floating functions that takes somethhing as an argument.

Still note that there is no magic involved in C++ polymorphism (or any other aspect of it, for that matter).

If you need polymorphism you need to implement it. C++ just makes that sooo much easier, and all the tech details are hidden from me (but I still have an idea about whats going on, much like I have an idea about what what machine code is generated when programming in C). Once, in the early days of OO, I read a scientific paper on how to do OO in assembly. This is quite possible, although it will display even more of the recurring tech details on the general implementation of eg polymorphism.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohanEkdahl wrote:

Answer 1: Exercise: Assume that you have a struct type, and an array of such structs. The data in the struct is to be be printed to a report, but how each struct is displayed depends on if the output is going to a printer or if it is going to the screen. Sketch how to solve this in C in a solution using printf.

This is not immediately solvable by printf. And I think it's a good example of the difference between C and C++ and the thinking in objects.
I think that the problem "regular" C coders have with this is that they think "How do I get printf to handle this or do this".
The "secret" which is obvious to the C++'er, is that printf (or whatever output function you have) is not the one printing the data - the struct is.
And that's probably the other hurlde - a struct printing data ???? Are you kidding?
No, not in C++, where a class (that CAN print) is basically just a collection of a struct (data) with functions (operations).
To solve it with printf we'd probably have to add a sort of callback method/specifier to the printf format. Which would eventually lead to writing code that is very similar to the way C++ actually handle the issue.

/Jesper
http://www.yampp.com
The quick black AVR jumped over the lazy PIC.
What boots up, must come down.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also, things like this in C

Quote:
theVehicle->functionTable->Drive(theVehicle);
are more likely to cause cache misses. This can be a huge performance hit on mutli-core architectures.

Obviously if the programmer implementing the above was aware when designing it wouldn't be a problem... Based on the wide variety of coders I've worked with, sadly, that 'if' becomes enormous.

You're free to write code like that if you need to in C++.

I have too many hobbies.
s-conductor.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For me, there I find little need for the advantages that C++ might give me in a microcontroller environment. I have no problems using C++ in an embedded environment, say under Linux where I'm more likely to be dealing with structured data and lots of it. I might need to support a number of tcp/ip sockets sending packetised data under Linux but only have one serial port and some i/o tied together with some sequential logic on a microcontroller. With asm vs C, you get quickly bored with calculating indexes etc using asm whereas in C the compiler does the mundane work for you a similar need for C++ doesn't exist for microcontroller apps for me. At least for the type of microcontroller apps I write - industrial controls and instrumentation. Another little hurdle to using C++ is its not supported under MISRA C rules.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Interesting.... I haven't (yet) done an avr project in C++, mostly because there are few times where I haven't needed the features.

However, I do miss it at times. Where I miss it most is where there is no AVR specific parts to the code I'm writing (i.e. no register access, communication to the outside world except through abstracted interfaces "UART_SendByte(...)".

In these instances, I miss the very clean and explicit interface that the class structure provides and find that I replicate it in C, often with the first argument being a pointer to the struct I am operating on. Functionally equivalent, but somehow not as explicit.

-- Damien

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I in general do understand and like the OO stuff, but I think I'm ruined by having learnt procedural programming first :)

In the past I've done a few Java projects and I read this ebook, IIRC, "Java design patterns" and it describes uses of OOP in all kind of clever ways I never could think of. Many things seemed impossible to do with plain C.

On a sidenote, VHDL seems almost impossible to understand an learn for pure software people with zero hardware knowledge. VHDL is a completely different kind of language; C/C++ are imperative languages, while VHDL is a dataflow language. This difference is maybe just as big as OOP vs structured.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The standard reference for design patterns is http://www.amazon.com/Design-Pat...

It's "K&R" of the design pattern world and essential for looking at C++ as C++, not just as "C with Classes"

-- Damien

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've been having another think as to why it bothered me about missing C++ on the AVR... Test frameworks!

I think I made the point that I missed C++ whenever I didn't have to do any AVR-specific code in a block of functionality. Logically, this code should be able to be compiled and run on a host PC, subject to word length, alignment and endian-ness issues.

If this is the case, then it could well be that I missed the security blanket of writing unit tests for the code (e.g. boost::test, cppunit) for which several frameworks exists for C++.

Unfortunately, I'm yet to come across a good testing framework for C with the AVR in mind (especially for AVR specific stuff), where the code can be regression tested automatically every time it's checked in.

Suggestions are welcome. What do you do for unit testing on the avr? Particularly automated regression testing?

-- Damiem

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One of the things that makes C++ less useful on small micros is the objects that would be modeled. In C++ you are modeling concepts such as a button in a GUI. Once you have it, you can make as many buttons as you want or make variants of the button for specialized purposes. In micros the most useful thing to model is the actual hardware peripherals. But if you model a timer, you can't make as many as you want, you can only match them directly to the actual timers you have, and they can only have the capabilities that are actually in hardware. And if you make objects for PWM outputs or ADC inputs, that doesn't really work since the objects use shared hardware underneath, making the "objectness" of them pretty much worthless.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Koshchi wrote:
One of the things that makes C++ less useful on small micros is the objects that would be modeled. In C++ you are modeling concepts such as a button in a GUI. Once you have it, you can make as many buttons as you want or make variants of the button for specialized purposes. In micros the most useful thing to model is the actual hardware peripherals. But if you model a timer, you can't make as many as you want, you can only match them directly to the actual timers you have, and they can only have the capabilities that are actually in hardware. And if you make objects for PWM outputs or ADC inputs, that doesn't really work since the objects use shared hardware underneath, making the "objectness" of them pretty much worthless.

I partially, but not entirely agree. The linux kernel (especially the file system and drivers) employs an object-like model, even when talking to real hardware.

So far, I've found it easier to employ C libraries for hardware functions on the AVR rather than an explicit object model, but I remember seeing once an excellent template example for a device that had multiple USARTs (Johan?) that avoided repeated code.

-- Damien

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here are a couple of examples that might be easier with objects... how about a flight simulator for a cessna in c... big loop of subroutines.. read controls, call routines that update engine, aircraft attitude, output to instruments,indicators. Now if the customer ordered up a copy for a two engine aircraft, I guess you'd save something by creating the second engine with new. I suppose this savings would be even greater in a C130 simulator (4 engines). I've been wanting to build a 'digital pa' with 2 computers... one on stage that does signal processing mic in, eq, mix, output, and a control computer out in the house that has VU meters and faders. No audio over the control link, just VU levels out to the Front Of House and control levels back. Now on a Real console, you have 16 or 24 or 32 identical input modules with a couple dozen knobs, and my software engineer half of the brain figured an input module and a submaster module would be good software modules. I got that clue because they both use the word 'module'. I havent gone much beyond this theoretical concept with this project. Want to help with it?

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Eliminating
memcpy()
and
sprintf()
and
free()
and these "dangerous" functions may be a benefit of OO or C++?

I've done C so long that I am very careful to avoid overruns.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

stevech wrote:
Eliminating
memcpy()
and
sprintf()
and
free()
and these "dangerous" functions may be a benefit of OO or C++?

I've done C so long that I am very careful to avoid overruns.

No so much eliminating, but ensuring that they're used when they need to be.

For example, consider some class:

class String
{
    public:

        String(const char *c_string); // Create a string
        ~String();

    protected:

        int m_length;
        char* m_array;
};

Since the destructor ~String() is automatically called when the instance of the class goes out of scope, we can do something like:

String::~String()
{
    if (m_array)
        free(array);
}

Which means it frees the user of the responsibility of manually managing the memory underneath (provided that the instance of String itself not dynamically allocated).

For an AVR, where every byte of ram counts, this may not be desirable. On desktop systems, where strings are used hundreds or thousands of times, it can reduce the probability of having a memory leak in your program.

If you think you're above that and never make a mistake freeing memory, have a thought for your successor and read this in the meantime: http://www.parashift.com/c++-faq...

-- Damien

p.s. You shouldn't be making your own string class anyway - that's what std::string is for...

p.p.s. new and delete largely supersede malloc() and free(), but the logic here is the same.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Now if the customer ordered up a copy for a two engine aircraft, I guess you'd save something by creating the second engine with new

But the real utility here would not be creating an object that was an engine. Rather, an "engine" would be composed of a large number of sub-objects composited together. Many of these objects might appear multiple times in one engine, or be common parts to several different types of engines.

One of the roads to bad design in OO programming is to try and create everything with a large tree of object types to cover all circumstances. But most designs are better handled by compositing a multitude of simpler objects, just as in real life you would build an engine out of parts rather than extrude the whole thing as one object. There would still be an engine object, but its interface would be rather simple. The performance characteristics of the engine would be given by the sub-objects.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One common theme between here and the assembly thread on the same topic seems to be the reluctance to let the compiler deal with the implementation of the semantics.

In OO, the idea is to hide the implementation as much as possible. Interaction with the object should be done through an interface, not by modifying the implementation.

By implication, the actual implementation can be changed at any time without changing the interface. This is a Good Thing (TM) - improvements can be made without altering the code that interacts with it.

Can I also guess that many here in the avr world (at least as far as programming is concerned) individually, or at most, in small teams. One thing I've experienced (ymmv) is that once the code base becomes large enough, you simply cannot wrap your head around all implementation details (and neither should you have to) - your interaction should be with the publicly defined interfaces.

Thoughts?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
In OO, the idea is to hide the implementation as much as possible. Interaction with the object should be done through an interface, not by modifying the implementation.

But you don't need OO for that per se. You can achieve the same thing with regular C (or even ASM for that matter) and structured programming.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jayjay1974 wrote:
Quote:
In OO, the idea is to hide the implementation as much as possible. Interaction with the object should be done through an interface, not by modifying the implementation.

But you don't need OO for that per se. You can achieve the same thing with regular C (or even ASM for that matter) and structured programming.

True, but OO tends to enforce it more rigorously,

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Put it this way what can you do with << that I simply cannot do with an invocation of (s)printf()?
Nothing. but then you can do it all with assembler or hand-written machine code too. However, I think it was a silly idea to overload the << operator for streams like this. People inexperienced with C++ (which I guess was pretty much everybody, at the time this set of classes was written) tend to see operator overloading, think "Ooh, that's cool" and you end up with classes that overload operators in non-intuitive ways, just because they can.

In the present case, the most significant advantages the operator<< syntax has over printf() is that it removes the variadic function call, and hence allows compile-time type checking (C doesn't really have any for variadic function calls), but this could have been achieved in different ways.

C++ can also be written so as to automate type conversions. This has been done for the stream IO classes (ie you can write

float f=3.1415926;
cout << f;

and the float is automatically converted to a string before being output, the conversion function being selected automatically depending on the type of f, this choice being made at runtime if necessary) but it is not necessary to use operator overloading in order to get the automatic type conversion.

CH
==
PS One of the silliest examples of operator overloading I saw was a class that attempted to do colour mixing with syntax something like

C_colour c1 = C_colour::Red;
C_colour c2 = C_colour::Green;

C_colour c3 = c1 + c2; // evaluates to C_colour::Yellow
C_colour c4 = c3 + C_colour::Blue;
c4 -= c2; // now we're left with C_colour::Magenta 

Just don't.

Last Edited: Wed. Mar 31, 2010 - 11:30 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

damien_d wrote:
So far, I've found it easier to employ C libraries for hardware functions on the AVR rather than an explicit object model, but I remember seeing once an excellent template example for a device that had multiple USARTs (Johan?) that avoided repeated code.
Maybe my example on the third page of this thread: https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=80632&start=40&postdays=0&postorder=asc&highlight=. It's the second of my two long posts to that thread, and compares a simple driver for four identical UARTs written in C by Bob Gardner, and by me in C++. It's a bit of a toy to be honest, but makes an interesting point, I think, and I have certainly used things like this on MCUs as small as a mega48 (I've used C++, classes and objects on a Tiny13!).

CH
==

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

The linux kernel (especially the file system and drivers) employs an object-like model, even when talking to real hardware.

But remind me again exactly how many .cpp files are to be found in the Linux kernel? If I look at the 'fs' directory:

=========================================================================================
root@DevSystem:/usr/src/linux-source-2.6.31/fs# ls
9p                  buffer.c             fifo.o          locks.o        readdir.o
adfs                buffer.o             file.c          Makefile       read_write.c
affs                built-in.o           file.o          mbcache.c      read_write.h
afs                 cachefiles           filesystems.c   mbcache.o      read_write.o
aio.c               char_dev.c           filesystems.o   minix          reiserfs
aio.o               char_dev.o           file_table.c    modules.order  romfs
anon_inodes.c       cifs                 file_table.o    mpage.c        select.c
anon_inodes.o       coda                 freevxfs        mpage.o        select.o
attr.c              compat_binfmt_elf.c  fscache         namei.c        seq_file.c
attr.o              compat.c             fs_struct.c     namei.o        seq_file.o
autofs              compat_ioctl.c       fs_struct.o     namespace.c    signalfd.c
autofs4             configfs             fs-writeback.c  namespace.o    signalfd.o
bad_inode.c         cramfs               fs-writeback.o  ncpfs          smbfs
bad_inode.o         dcache.c             fuse            nfs            splice.c
befs                dcache.o             generic_acl.c   nfs_common     splice.o
bfs                 dcookies.c           generic_acl.o   nfsctl.c       squashfs
binfmt_aout.c       dcookies.o           gfs2            nfsctl.o       stack.c
binfmt_aout.ko      debugfs              hfs             nfsd           stack.o
binfmt_aout.mod.c   devpts               hfsplus         nilfs2         stat.c
binfmt_aout.mod.o   direct-io.c          hostfs          nls            stat.o
binfmt_aout.o       direct-io.o          hpfs            no-block.c     super.c
binfmt_elf.c        dlm                  hppfs           notify         super.o
binfmt_elf_fdpic.c  drop_caches.c        hugetlbfs       ntfs           sync.c
binfmt_elf.o        drop_caches.o        inode.c         ocfs2          sync.o
binfmt_em86.c       ecryptfs             inode.o         omfs           sysfs
binfmt_flat.c       efs                  internal.h      open.c         sysv
binfmt_misc.c       eventfd.c            ioctl.c         open.o         timerfd.c
binfmt_misc.ko      eventfd.o            ioctl.o         openpromfs     timerfd.o
binfmt_misc.mod.c   eventpoll.c          ioprio.c        partitions     ubifs
binfmt_misc.mod.o   eventpoll.o          ioprio.o        pipe.c         udf
binfmt_misc.o       exec.c               isofs           pipe.o         ufs
binfmt_script.c     exec.o               jbd             pnode.c        utimes.c
binfmt_script.o     exofs                jbd2            pnode.h        utimes.o
binfmt_som.c        exportfs             jffs2           pnode.o        xattr_acl.c
bio.c               ext2                 jfs             posix_acl.c    xattr_acl.o
bio-integrity.c     ext3                 Kconfig         posix_acl.o    xattr.c
bio-integrity.o     ext4                 Kconfig.binfmt  proc           xattr.o
bio.o               fat                  libfs.c         qnx4           xfs
block_dev.c         fcntl.c              libfs.o         quota
block_dev.o         fcntl.o              lockd           ramfs
btrfs               fifo.c               locks.c         readdir.c

Last Edited: Wed. Mar 31, 2010 - 11:46 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:

The linux kernel (especially the file system and drivers) employs an object-like model, even when talking to real hardware.

But reminded me again exactly how many .cpp files are to be found in the Linux kernel?

Use of an object-based design model does not imply (and is not implied by) use of a C++ compiler.

CH
==
PS Perhaps this is the point you are trying to make?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Use of an object-based design model does not imply (and is not implied by) use of a C++ compiler.

My point exactly!

I want to avoid a "war" but I keep hearing that C++ allows you to do stuff in "large projects" that you cannot do with C and that you would have real problems trying to do such large projects in C.

Yet Linux does and the 50,000+ file projects I worked on for digital TV systems do - it's all in C, not C++ but we use very modular structures to isolate functionality in the same ways that I beleive C++ imposes into the design.

If I:

root@DevSystem:/usr/src/linux-source-2.6.31# find . -name \*.[ch] | wc -l
36770

So Linux is a system with 36,000+ source files.

Last Edited: Wed. Mar 31, 2010 - 11:32 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

But remind me again exactly how many .cpp files are to be found in the Linux kernel? If I look at the fs directory:

Precisely zero; the point being that OO is not the exclusive domain of languages that support this feature (though it is generally easier to implement in languages that support this feature)

The filesystem layer is an excellent example of an abstract interface where the actual code that is executed is determined at runtime - sounds like polymorphism to me...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I once browsed through the source of a CANopen stack. It emulated OOP with truckloads of intricate and incomprehensible preprocessor magic :)

Anyone using (exotic) techniques like coroutines?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
I keep hearing that C++ allows you to do stuff in "large projects" that you cannot do with C
Not "cannot", but object-based software models (whether implemented in C, C++ or whatever) do encourage the sort of code that makes large projects reliable and maintainable. I suggest that the example of the linux kernel's use of an object-based design for the filesystems is a reasonable demonstration of this.
clawson wrote:
we use very modular structures to isolate functionality in the same ways that I beleive C++ imposes into the design.
Not "imposes" but "encourages". You can write spaghetti code in C++ just like you can in any other language! In fact, writing unintelligible, obfuscated code [1] in C++ is so easy it just isn't fun.

CH
==
[1] http://en.wikipedia.org/wiki/International_Obfuscated_C_Code_Contest

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So if I can write in a modular "object" design methodology in C then where's the incentive to move to C++? Is it simply because it standardises the concepts?

I guess this now starts to mirror the C/Asm thread - part of the reason to use C rather than Asm is that it formalises the layout structure for constructs like for(), while(), if() that Asm programmers were already using but each programmer had their own way - (do you do the positive test and jump if negative, do you just skip a long jump...). Maybe C++ just formalises things like the "data hiding" that you might already be doing in C by keeping data in as limited a scope as possible - possibly only accessible with specific access routines. (is that what you call "Ecapsulation"?).

With things like polymorphism (which I believe is the way a function such as add() could be passed parameters of different types and still be able to add them whatever type they were?) I've never really followed why this is useful? Sure you may have different routines in C so that one handles parms of type_A and the other handles parms of type_B (I'm just thinking of AVR-LibC's pgm_read_byte/word/dword() routines as I type this) but what's the disadvantage of this? Is it that the three functions effectively have the same core (but just differ in reading 1/2/4 bytes) and therefore if you want to change the behaviour you have to fix it in 3 places? Is that why this is seen as an advantage in C++?

How about inheritance? That's where you write something, fully debug it then to later change it's behaviour you just base new routines on the underlying (tested, unchanging) base class isn't it? Just modifying the behaviour of some aspects for the new implementation. But how does this differ from C? If you write modular C and once a block is tried tested and approved you don't change it then that's the same isn't it? I mean when did anyone last change malloc() or free() in a C library? Or sin()/cos()/tan()? You may want to write some mathemetical function that is like cos() but (say) offset by 45 degrees then you call on cos() for the core of this. Is this inheritance? How does C++ make this "better"?

At the bottom line I guess I'm just a luddite and I'm never going to "get" this. The switch from Asm to C was bad enough - maybe this is just a step too far for an old bit mangler?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

So if I can write in a modular "object" design methodology in C then where's the incentive to move to C++? Is it simply because it standardises the concepts?

Add at least these:
- The C implementation will be weaker w.r.t. eg type safety etc than the C++ implementation. The OO is built into the language rather than being a part of your implementation.
- C++ classes offers constructs for eg access or not to objects (C structs do not).
- C++ offers a clearer syntax (eg theVehicle->funPtrTable->Drive(theVehicle) in C v/s theVehicle->Drive() in C++).
- The compiler C++ will check stuff for you, that will be much tougher to control in a C implementation. Example: If you in a base class declare a pure virtual function, the compiler will emit an error if you do not implement it in the inheriting classes. When you "manually" implement OO stuff in C it is also your responsibility to check those things, maybe even not being able to detect the mistake until run-time.

The argument you are trying to make have great structural similarities with "So if I can write structured code in assembler, then where's the incentive to move to a high level language?".

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

The argument you are trying to make have great structural similarities with "So if I can write structured code in assembler, then where's the incentive to move to a high level language?".

You are absolutely right about that.

But the difference (as I see it anyway) is that I can see the advantages of C over Asm (for the way I use it anyway). I just cannot see the advantages that are supposedly offered by C++ and don't particularly want to have to learn some tortuous, impenetrable syntax to avail of them.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A good example of why polymorphism is useful is the so-called observer pattern - I'll use an example to explain.

I have a class that passes GPS sentences. I have several things (which may vary, depending on what command line options I give the program) wanting to know whenever a new GPS sentence is available.

// In reality, this will be a c-like struct that holds the a position as private members.
// Either way, get get the latitude and longitude through accessor functions, NOT from the internal data
class Position
{
    public: 
        double Latitude() const;
        double Longitude() const;

   protected:
       /* Implementation not important in this example */
};

In this case, I have a navigation system wanting to know about the position updates, a GUI that wants to know about position updates (if we're not in headless mode) and a logger to store position updates (if we've set the system to log). They all observe GPS sentences as they arrive:

class PositionObserver
{
    public:
        virtual ~PositionObserver() {} // to avoid memory leaks in derived classes;
  
        // Abstract function: has no implementation
        // must be implemented by derived classes
        virtual void SetPosition(const Position &location) = 0;
};
  
class NavigationSystemObserver : public PositionObserver
{
    public:

        // Does navigation stuff
        virtual void SetPosition(const Position &location) { /* ... */ }
};

class LoggingSystemObserver : public PositionObserver
{
    public:

        // Does logging stuff
        virtual void SetPosition(const Position &location) { /* ... */ }
};

class GUIObserver : public PositionObserver
{
    public:

        // Does GUI stuff
        virtual void SetPosition(const Position &location) { /* ... */ }
};

Now, the sentence provider (whatever that may be) wants to send sentences to all observers subscribed to it but it does not care what they are and does not change, even if I add more observer types. To do so, it maintains a list of base class pointers:

class SentenceGenerator()
{
    public:

        // Destroys all subscribed observers when it goes out of scope
        ~SentenceGenerator();

        // When it gets a new sentence, it will send it to all subscribers
        bool WaitForNewSentence();

       // Register an observer with the sentence generator
       void RegisterSubscriber(PositionObserver *observer); 

    protected:

        std::list m_observerList;
};        

void SentenceGenerator::RegisterSubscriber(PositionObserver *observer) 
{ 
    m_observerList.push_back(observer); 
}

void SentenceGenerator::~SentenceGenerator()
{
    for (std::list::iterator it = m_observerList.begin() ; it != m_observerList.end() ; ++it)
    {
        delete (*it);
    }
}


bool SentenceGenerator::WaitForNewSentence()
{
    Position pos;
    if (!SomehowGetPositionFromGPS(pos))
        return false;

    // We have a sentence - send it to all observers.  Note that it has **no idea**
    // what the individual observers actually are! (i.e a logger or whatever)
    // Note also, it does not care how many observers are subscribed
    for (std::list::iterator it = m_observerList.begin() ; it != m_observerList.end() ; ++it)
    {
        it->SetPosition(pos);
    }

    return true;
}

So, at runtime, it might look something like:

int main(int argc, char **argv)
{
    SentenceGenerator generator;

    if (ArgumentsHaveGUI(argc, argv))
        generator.RegisterSubscriber(new GUIObserver());

    if (ArgumentsHaveLogger(argc, argv))
        generator.RegisterSubsriber(new LoggerObserver());

    // Always have a navigation observer
    generator.RegisterSubscriber(new NavigationObserver());

    // Now, dispatch sentences to all observers until the generator fails
    while (generator.WaitForNewSentence())
    {
    }

    // No need for memory cleanup - the dynamically created objects are deleted (freed)
    // by the destructor of the sentence generator 
    return 0;
}

There's an example of polymorphism in action and why it can be useful. Whilst no doubt a solution for this exists in C, quite a few C++/Java/Flavour-of-month programmers will recognize this as written

-- Damien

p.s. improvements welcome :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

tortuous, impenetrable syntax

What language are we talking about now, then? :wink:

As I have pointed out, the syntactic constructs needed to get an OO-oriented approach implemented in C is quite uglier than the C++ one.

Quote:

don't particularly want to have to learn [...]

Can I tickle your curiosity in some way, then? Or maybe say that if you learn the basics of C++ you are 2/3rds done with both Java and C#?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
So if I can write in a modular "object" design methodology in C then where's the incentive to move to C++?
C++ has other features besides encouraging encapsulation. For example, constructors and destructors, references, templates, exceptions, strong compile-time type checking etc etc.

Quote:
With things like polymorphism (which I believe is the way a function such as add() could be passed parameters of different types and still be able to add them whatever type they were?) I've never really followed why this is useful? Sure you may have different routines in C so that one handles parms of type_A and the other handles parms of type_B (I'm just thinking of AVR-LibC's pgm_read_byte/word/dword() routines as I type this) but what's the disadvantage of this?
Having more than one function of the same name differentiated by the types of the parameters they take is called function overloading. It means you can write generic code that does not necessarily need to know about the types it is operating on, provided each potential type provides a suitable function of the pre-agreed name that implements appropriate behaviour.

Quote:
How about inheritance? That's where you write something, fully debug it then to later change it's behaviour you just base new routines on the underlying (tested, unchanging) base class isn't it? Just modifying the behaviour of some aspects for the new implementation.
Inheritance is more usually extending behaviour, not modifying existing behaviour (though in some cases that might be a good thing to do). Founding complex behaviour upon tried and tested base behaviour, and being required to access that base behaviour only through a defined public interface, is clearly a sound principle to follow.

The typical noddy example given is to have a base class "shape" which then has classes like "square", "circle" and "triangle" derived from it. You can then make an array of pointers to shapes, and draw them all with a simple loop.

class Shape {
public:
   // every shape must have a Draw() method
   // but there is no default, so make the
   // base class "pure virtual" like this:

   virtual void Draw(void) = 0;

   //... lots of stuff missing

private:
   int x;
   int y;
};

class Circle : public Shape {
public:
   // again, stuff missing

   virtual void Draw(void) {
      // code here to draw a circle
      // radius r, centre x, y
   }

private:
   int r;
};

class Square : public Shape {
public:
   // again, stuff missing

   virtual void Draw(void) {
      // code here to draw a square
      // side s, centre x, y
   }

private:
   int s;
};


Shape* s[10]; // an array of pointers to shapes

//...

for (int i=0; i<10; i++) {
    s[i]->Draw(); // draw shape i. 
}

The loop that draws the shapes is completely agnostic as to whether each shape is actually a square or a circle. The compiler automatically generates code to work that out at run-time if it can't be done at compile-time. "CODE BLOAT" I hear you cry! I counter that the resulting code is insignificantly bloated (if at all) compared to the same functionality implemented in (many more lines of) C.

This is a noddy example, but I have used essentially the same idea to good effect in many places in embedded applications. I could go into examples in more detail if you wish.

CH
==

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Since we're not having a war here, I'll add to the not-war by offering the opinion that modern Ada is a much better C++ than C++ is, and with strong concurrency and realtime features to boot.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Or maybe say that if you learn the basics of C++ you are 2/3rds done with both Java and C#?

But I already know Java (well a fair bit of it). Syntactically it seems more obvious to me than C++

But Maybe this is the same "syntax blindness" that puts Asm programmers off using C with all its <<, *, &, -> and so on?

I follow :: and ~ (deconstructor) and various other C++ bits but there's some syntax (at least one involving < >) that I simply cannot see the logic of.

EDIT: found it - it's called "function templates": http://www.cplusplus.com/doc/tut... - what's that all about then?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But you can program all your life in C++ and never need templates. The book "Effective C++" describes the language as 4 parts: C, C++ proper, templates and STL. C you already know, and templates and STL you don't need to use if you don't want to (just as you don't need to use standard library calls in C).

Templates are useful for creating generic objects that have a defined behavior, but not a defined datatype. If you wanted, for example, to make a list object that keeps a doubly linked list of objects, the behavior of the list would not depend on the type of object being held in the list. This could be done in C by using void pointers to the actual objects, but in doing so you can put any type of object into that list. To restrict this to storing only a specific type in the list you would need to change the code to enforce that restriction. With templates, you can define List to handle generic type, then simply declare an instance of a list to handle a specific type like this:

List integerList; // A list of integers
List floatList; // A list of floats

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For me it is the strong typing and generic programming ( template ) capabilities of C++ that are most attractive to me. I am no slouch when it comes to programming but I feel better knowing that the compiler concurs with me that the types I think I am using I actually am using. Also, quite a bit of my work ( embedded and PC based ) is computation heavy and, using the dreaded templates, I can write generic algorithms that will operate on different data types. I'm not talking about simply changing the data type of an input variable, or even a few casts - that is clearly easy enough with the preprocessor - I change the functionality of the actual functionality of the algorithm when the datatypes change. Perhaps an example is in order.

I am working on a fixed-point number class ( yes, I do realize that progress is being made in avr-gcc fixed-point support, bare with me ). The class is based on templates and, therefore, can have different storage type and attributes set at compile time. Initially assume that only the very basic operations are to be supported: addition, subtraction, multiplication, and division. There are several formats that should be supported as well: 0.8, 1.7 and, perhaps, 2.14. Then you want to be able to combine inputs and outputs freely. Using either the standard C route of separate function ( and names ) OR the C++ form of function overloading you would then have 4 * 3 * 3 * 3 = 108 ( four operations and each return value and operand can be of different types ) functions to write. Of course, neither C nor C++ allows you to overload on return value, but we'll ignore that for now ( yes, there is a way around that as well... ). In C++, using templates, I just write four functions... four. I write addition, subtraction, multiplication and division once each. I can do that because, even though each of those 108 functions is different, their forms are dependent on information that is available at compile time. Therefore the function is written to function based on the information and I get the functions ( and only the functions ) I need in any program. For me this is abundantly worth the, as has been ( fairly rightly ) said, torturous syntax of templates.

It should be noted that those functions and the data that they are operating on are, of course, strongly typed and the compiler is checking that too. Still, not everyone needs templates. I would, indeed, argue that few people really do and it is those that KNOW they need them, that should be using them. Yes, that is a somewhat cheeky attitude, but I'm not sure of a better way to approach it. It is similar to the use of malloc/free in C on a micro-controller. Yes, it can be done, sometimes it should be done, but if you don't know why, don't do it.

I revel in the incomprehensible parts of C++ because it allows me to do the things that I need to do most efficiently and effectively. I would argue that the other parts of C++ though are the parts that have more reasonable syntax and are not so mysterious. YMMV.

Martin Jay McKee

P.S. If there's a desire, I could construct a small example when I have a bit more time. I know that code is sometimes a more native language than English when it comes to these things.

As with most things in engineering, the answer is an unabashed, "It depends."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
but there's some syntax (at least one involving < >) that I simply cannot see the logic of.
It hasn't much to do with logic. The C++ designers simply ran out of simple syntax phrases. Every new feature of the language has to be re-fitted into the existing syntax, so no existing code breaks (doesn't suddenly get another meaning).

So the syntax of newer features is more convoluted than that of old features. And for templates they came up with an IMHO rather disgusting < > syntax in all sorts of inconvenient places (NB: Java unfortunately copyed part of the convoluted C++ template syntax for Java generics :-()

Quote:
EDIT: found it - it's called "function templates": http://www.cplusplus.com/doc/tut... - what's that all about then?

You mean the following?

template 
myType GetMax (myType a, myType b) {
 return (a>b?a:b);
}

Thats templates used as glorified macros, with automatic macro expansion when needed.

In C, if any C programmer would bother, one would fake it as follows

// Macro to create GetMax_xxx functions
#define TEMPLATE_GETMAX(myType)                   \
    myType GetMax_##myType(myType a, myType b) {  \
        return a > b ? a : b;                     \
    }

// Create GetMax_int(), a function to return the maximum
// of two integers
TEMPLATE_GETMAX(int)

// Create GetMax_double(), a function to return the maximum
// of two doubles
TEMPLATE_GETMAX(double)

// Later in the code ...

int a = 1;
int b = 2;
int m = GetMax_int(a, b);

double aa = 1.0;
double bb = 2.0;
double mm = GetMax_double(aa, bb);

Note, the C version is a fake. The C++ template version doesn't need any explicit creation of the functions. They are created when used - e.g. when someone first calls GetMax(int, int) the C++ compiler creates the function. Also, the C++ compiler choses an internal name for the function, in accordance with its name mangling schema. So the internal name of the GetMax(int, int) function is likely not that of the C-version's GetMax_int(int, int). But that doesn't matter, since only the C++ compiler needs to know the internal name. The programmer always just uses calls to GetMay(int, int).

The C++ version has a few more advantages in some circumstances. E.g. a good C++ compiler would recognize that GetMax(uint8_t, uint8_t) is the same as GetMax(unsigned char, unsigned char), and just create one function for both usages.

Stealing Proteus doesn't make you an engineer.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ArnoldB wrote:
clawson wrote:
but there's some syntax (at least one involving < >) that I simply cannot see the logic of.
It hasn't much to do with logic. The C++ designers simply ran out of simple syntax phrases. Every new feature of the language has to be re-fitted into the existing syntax, so no existing code breaks (doesn't suddenly get another meaning).
The added syntax and semantics did not have to be so backwardly similar to C (or as close as that got) -- Bjarne Stroustrup felt it had to be that way and so, with a lot of work, that is he how he made it. Take a look at Objective-C to see how adding a little foreign inspiration and expression (in this case from Smalltalk) greatly simplifies a language. One language takes most people something like 6+ months to get proficient in; the other takes a few days and is more backwards compatible as well!

I write code in C, C++ and Objective-C, and can understand how C++'s complicated syntax discourages its use. I think this is a great pity as you can do many wonderful things with it and Objective-C.

- John

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

But I already know Java (well a fair bit of it).

B-b-b-but Java is full of all this stuff that we've been talking about - eg the java streams are a prime example of what you can do in an OO language and environment?!? (That "decorator" pattern I mentioned is the prime influence for that design of a part of the Java class library IIRC).
Quote:

Syntactically it seems more obvious to me than C++

Absolutely, but I can not in any way see how that would be major (or even a minor) impact on ones understanding of a language.

If you know Java, then (apart from some of the syntax), you know 2/3rd of C++.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]