Followers 0

# why is C++ not commonly used in low level code?

## 68 posts in this topic

[quote name='Calmatory' timestamp='1311003538' post='4836842']
This is because you don't care about portability. You only wanted your ircd run on your own system.

I myself write code which has to run from routers to super computers, so anything except C is a definite no-go for me, because the code HAS to compile on those machines which run operating systems and CPU and hardware architectures unknown to me. Everything I care about is conforming to C89, POSIX and SUS(Single Unix Specification) standards.

[/quote]

I am sorry but this is an incorrect and hastily entered response. The software in question is extremely popular, in use by many hundreds of networks, and definitely not just designed 'for my own computer'. I myself run a 600 user network on it and there are many other networks of 3000 users and up that use the same software. Maybe you should take a look at the project website (http://www.inspircd.org) and reconsider your kneejerk response to my post.
2

##### Share on other sites
Moved to the lounge, a more appropriate venue for "C vs C++" or any of the other common examples of X vs Y you can think of.
0

##### Share on other sites
Argh. I wrote a long-ish post for almost an hour. Topic moved and the forum itself being rather unfriendly(messing the text layout and indentations), dundun. Since I'm in a hurry I'll just leave these here for now:

Here's a simple example:

[code]

C:

#include <stdio.h>

int main(int argc, char *argv[]) {
int i=0;
int j=0;
char name[32];
for(i=0;i<8192;i++) {
printf("Argument count: %d\n", argc);
for(j=0;j<argc;j++) {
printf("Name: %s\n", argv[j]);
}
}
scanf("%32s", (char*)&name);
printf("Hello %s!\n", name);
return 0;
}

C++:

#include <iostream>
#include <string>

int main(int argc, char *argv[]) {
std::string name;
for(int i=0;i<8192;i++) {
std::cout << "Argument count: " << argc << std::endl;
for(int j=0;j<argc;j++) {
std::cout << "Name: " << argv[j] << std::endl;
}
}
std::cin >> name;
std::cout << "Hello " << name << "!" << std::endl;
return 0;
}

[/code]

results:

[quote]

[calmis@purpleorange cvscpp]$g++ main.cpp -o main2 -O0 -Wall -Wextra [calmis@purpleorange cvscpp]$ gcc main1.c -o main1 -O0 -Wall -Wextra
[calmis@purpleorange cvscpp]$time echo "Dun-dun Daa" | ./main2 a ab abc abcd abcde abcdef abcdefg > mainCPP.txt real 0m0.314s user 0m0.063s sys 0m0.203s [calmis@purpleorange cvscpp]$ time echo "Dun-dun Daa" | ./main1 a ab abc abcd abcde abcdef abcdefg > mainC.txt

real 0m0.046s
user 0m0.023s
sys 0m0.000s
[calmis@purpleorange cvscpp]$wc -c main2 8048 main2 [calmis@purpleorange cvscpp]$ wc -c main1

5213 main1

[b]Size optimizations turned on: [/b]

[calmis@purpleorange cvscpp]$g++ main.cpp -o main2 -Os -s -Wall -Wextra [calmis@purpleorange cvscpp]$ gcc main1.c -o main1 -Os -s -Wall -Wextra
[calmis@purpleorange cvscpp]$time echo "Dun-dun Daa" | ./main2 a ab abc abcd abcde abcdef abcdefg > mainCPP.txt real 0m0.326s user 0m0.043s sys 0m0.227s [calmis@purpleorange cvscpp]$ time echo "Dun-dun Daa" | ./main1 a ab abc abcd abcde abcdef abcdefg > mainC.txt

real 0m0.035s
user 0m0.023s
sys 0m0.000s
[calmis@purpleorange cvscpp]$wc -c main2 5132 main2 [calmis@purpleorange cvscpp]$ wc -c main1
3508 main1

[/quote]

9.3x faster, some 1.4x smaller.

C functions with void* arguments and a variable to denote the types, and then using switch() to invoke right code(assuming that e.g. 8, 16, 32 and 64 bit ints can share the same code) is more size-efficient than C++ with templates.

I rest my case for now.

0

##### Share on other sites
I don't think you are able to measure the performance of a language with an artificial benchmark such as this. An artificial benchmark can be designed to prove that any X is 'better than' Y for any specific benchmark.

Every language has specific traits that outweigh some other language, and this is what matters. In the end, holy wars with 'X vs Y' do nothing but serve to cause arguments and usually are based in nothing but heresay and personal opinion without solid proof.

Use the right tool to solve the problem, without trying to use an excavator to swat a fly, and you will prosper. :-)

(EDIT: 1.6 million google results are [url=http://www.google.co.uk/search?hl=en&safe=off&client=firefox-a&hs=S9t&rls=org.mozilla%3Aen-GB%3Aofficial&q=why+benchmarks+are+useless&aq=f&aqi=&aql=&oq=]here[/url] saying why it is not a good idea to trust such benchmarks). Edited by braindigitalis
1

##### Share on other sites
Thanks for replies. I tried with \n, reserved some space for the string and disabled the sync with following results:

[quote]

[calmis@purpleorange cvscpp]$time echo "Dun-dun Daa" | ./main1 a ab abc abcd abcde abcdef abcdefg > mainC.txt real 0m0.035s user 0m0.017s sys 0m0.007s [calmis@purpleorange cvscpp]$ time echo "Dun-dun Daa" | ./main2 a ab abc abcd abcde abcdef abcdefg > mainCPP.txt

real 0m0.055s
user 0m0.033s
sys 0m0.003s

[/quote]

Obviously the speed bottleneck in this case is/was due to a incompetent programmer(me). However, there's still the size problem to solve. With GCC 4.6 the sizes are:

[quote]

[calmis@purpleorange cvscpp]$wc -c main1 3508 main1 [calmis@purpleorange cvscpp]$ wc -c main2
5944 main2
[/quote]

...when compiled with -Os and -s flags. This is my main point when working with embedded systems which do not necessarily have the resources to spare for increased code size.

But as stated numerous times before, the main reason for preferring C over C++(at least to date) has been with portability and habit.

[quote name='braindigitalis' timestamp='1311016143' post='4836957']
I don't think you are able to measure the performance of a language with an artificial benchmark such as this. An artificial benchmark can be designed to prove that any X is 'better than' Y for any specific benchmark.

Every language has specific traits that outweigh some other language, and this is what matters. In the end, holy wars with 'X vs Y' do nothing but serve to cause arguments and usually are based in nothing but heresay and personal opinion without solid proof.

Use the right tool to solve the problem, without trying to use an excavator to swat a fly, and you will prosper. :-)
[/quote]

This is completely true. It is not possible to draw conclusions about the whole language based on single benchmarks really. However, I was asked for an example which demonstrates that C indeed is more resource efficient than C++, and I brought in one, very simple example of this. And it gets even worse when one starts using more than just the standard C++ library, e.g. classes, multiple inheritance and exceptions. This is based on my experience when working with 1k/4k demoscene prods, in which C++ is a definite no-go because of the increased resulting binary size. If size doesn't matter(what!?), then C++ is much more attractive for me at least. But as shown above, I am not a C++ dev at all really.

0

##### Share on other sites
Did you remember to strip symbols and debug information from your binary after you compiled it?
1

##### Share on other sites
[quote name='Calmatory' timestamp='1311016150' post='4836958']
This is based on my experience when working with 1k/4k demoscene prods, in which C++ is a definite no-go because of the increased resulting binary size.
[/quote]

Ahh, the nostalgia, i remember when these were written in assembly language, not C
1

##### Share on other sites
[quote name='ApochPiQ' timestamp='1311016774' post='4836968']
Did you remember to strip symbols and debug information from your binary after you compiled it?
[/quote]

With external tools? Not really, but after doing that the results are as follows:

[quote]

[calmis@purpleorange cvscpp]$wc -c main1 3508 main1 [calmis@purpleorange cvscpp]$ wc -c main2
5944 main2
[calmis@purpleorange cvscpp]$strip -R .comment -R .gnu.version main1 [calmis@purpleorange cvscpp]$ strip -R .comment -R .gnu.version main2
[calmis@purpleorange cvscpp]$sstrip main1 [calmis@purpleorange cvscpp]$ sstrip main2
[calmis@purpleorange cvscpp]$wc -c main1 2048 main1 [calmis@purpleorange cvscpp]$ sstrip main2
[calmis@purpleorange cvscpp]\$ wc -c main2
4424 main2

[/quote]

The reason why I excluded this in the first place was that when working with embedded systems often there are no such tools to use, so they'd just skew the results.

[quote name='braindigitalis' timestamp='1311016824' post='4836970']
[quote name='Calmatory' timestamp='1311016150' post='4836958']
This is based on my experience when working with 1k/4k demoscene prods, in which C++ is a definite no-go because of the increased resulting binary size.
[/quote]

Ahh, the nostalgia, i remember when these were written in assembly language, not C
[/quote]

These days optimizing compiler can generate pretty much quite damn optimal assembly from C, and OS-specific tricks and external strippers/packers/crunchers have advanced very much during the past few years or so to further reduce the gap between C and asm. Writing a barebone sketch with C and optimizing the rest by hand in asm is the way to go.
0

##### Share on other sites
A big reason is that "embedded" systems span an incredible range of devices and processors -- typically, anyone with an odd-ball processor or who sells their own microcontrollers is also in the position of having to develop and maintain their own tool-hains, run-times, etc -- C is simply the easiest path, and probably about an order of magnitude less difficult to implement and support.

There's also a certain amount of historical bias / tribalism among codgy old embedded developers, who probably gave up assembly with a fair bit of reluctance. In the wide, this means that much of the ecosystem is built around C. C++ is making inroads on more capable platforms (eg, MIPS/PowerPC platforms), but C is still king elsewhere, and probably will be for quite some time.

It's also true that, when a custom solution is called for, its far more straight-forward to build something on top of C, than to try to undo/work around what C++ has by default and replace it with your own -- I'm not talking about memory allocation here, I mean things like providing some sort of (psuedo-)object system built to be performant on the device.

You're right that C++ does have quite a few other features that are useful to embedded dev though -- for example, you could concievably use placement new to initialize memory-mapped devices using the class constructor, and template meta-programming can be used to generate highly-tuned code parametrically.
2

##### Share on other sites
Mostly because C++ doesn't help solve the problems that "low level code" is facing. It's considerably easier to guess what's going on under the hood with a C program compared to a C++ program. Virtual functions make it difficult to determine what code will run next. Templates make it difficult to determine what code even exists. C++ tries to be type-safe, where C doesn't. Sometimes you pay a performance cost for this type safety, sometimes you don't, but afaik you never get a performance win out if it (like OCaml purports to do).

So, if you're writing code, and all of the domain momentum is geared toward C (existing code, libraries, developer skillsets), it's very difficult to justify moving to C++. It's a similar situation with C++ vs a higher level language for games.
0

##### Share on other sites
[quote name='Calmatory' timestamp='1311013068' post='4836928']I rest my case for now.[/quote]As mentioned by phantom, that's a completely false benchmark, as both programs aren't doing the same thing at all, you've made the C++ one do a ton more work than the C one.

You may as well have written:[code]//C benchmark
for( int i=0; i<1; ++i ){printf("");}[/code][code]//C++ benchmark
for( int i=0; i<10000; ++i ){printf("");}[/code][quote name='typedef struct' timestamp='1311024267' post='4837043']
Mostly because C++ doesn't help solve the problems that "low level code" is facing. It's considerably easier to guess what's going on under the hood with a C program compared to a C++ program. Virtual functions make it difficult to determine what code will run next. Templates make it difficult to determine what code even exists.[/quote]That's a completely false argument -- the C equivalent of virtual functions are function pointers. The C equivalent of templates are #include-templates.
If you used those same methodologies in both languages, you'd have the same problems in both languages.

Not all C++ programs are written using the wrong features at the wrong time. Just because you're using C++ everywhere, it doesn't mean you should go and use virtual for something where you wouldn't have used a function-pointer in C. If you do, then you're just a shit C++ programmer, and the argument becomes "[i]good programmers are better than shit programmers, derp![/i]".
2

##### Share on other sites
[b] With regards to Linus Torvalds and Linux:[/b]
0

##### Share on other sites
[quote name='phantom' timestamp='1311019641' post='4836996']
2 points;

1) std::cin and std::cout are doing more than the scanf and printf under the hood, which are both valid C++ functions btw and thus any coder worth their salt would use them if the situation required it*
2) As soon as the input string exceeds 32 characters your C code runs the risk of blowing up nicely; the C++ version is safe in that regard.

(* it becomes a speed vs safety trade off... if you want speed then you'd take the functions with the lowest overhead, if you want safey then you have to ditch some of that speed)
[/quote]

You are right, and I did this on purpose. As I mentioned before, with C++ I mean as "pure" C++ as it gets. It's no use to exploit as much C feature-set with C++ code and go all "But this IS valid C++ too!". There's no way to define when to stick to C library or when to use the C++ equivalents. Though, my main point for C is still it's portability(well supported on numerous platforms) and simplicity(Not so hard to write/port a custom compiler for custom hardware).

Since people have been critical towards the example I gave, I challenge others to write example code too for us all to learn more from.

0

##### Share on other sites
[quote name='Conoktra' timestamp='1311037849' post='4837125'][b]
[/b][list][*]C is used in Linux because of its versatility. It can do everything C++ can do, but also gives you fined-tuned control over [i]every[/i] aspect of program at the cost of expertise. This "theme" is the very core of Linux--to have a OS that is extremely versatile, but often times harder to run without the expertise (which is why the two work hand-in-hand). Here is a quick example of this:[list][*]In C++ it has built-in OO functionality via classes. This makes things such as polymorphism much easier. BUT, by using classes you loose control of how object pointers are handled in the member functions, you loose control of how constructors/destructors are called and when, how member-function pointers are stored within the structure, etc etc.[*]In C you can still do OO programming just like C++ classes by using structures and callbacks via function pointers. But, you get the ultimate control of how everything happens. YOU decide when constructors/destructors are called, YOU decide how object pointers are passed to member-functions, and YOU decide how member function pointers are stored in the structure and how they are alligned.[*]By having this fine-control in C it allows you to write extremely fast and efficient code, while still doing everything C++ does. Here are a couple of examples:[list][*]I could choose a special alignment of the function pointers that could produce huge performance boosts on certain systems.[*]I could choose to pass the object pointer to the member function via the stack instead of a register, thus saving the register for a different parameter--which when used right, could provide huge performance boosts.[*]Etc etc.[/quote]I know that you know that you can do all of that in C++ as well as in C....[/list][/list][/list]
So... all this really means is that a shit C++ programmer is worse than a good C programmer?
Shit is worse than good?
Ok.

[size="2"][font="arial, verdana, tahoma, sans-serif"][quote name='Calmatory' timestamp='1311042674' post='4837146']You are right, and I did this [wrote an invalid benchmark] on purpose. As I mentioned before, with C++ I mean as "pure" C++ as it gets.[/quote]You're just demonstrating that you don't know C++ very well then, as instead of using a heavyweight formatted and localized output stream, you could've used a lightweight one that was a better analog of [/font][font="Courier New"]printf[/font]... Or if the task really needed [font="Courier New"]printf[/font], a good C++ programmer might just use [font="Courier New"]printf[/font].[/size]
[size="2"][quote][/size][size="2"]It's no use to exploit as much C feature-set with C++ code and go all "But this IS valid C++ too!".[/quote]Why?[/size]
[size="2"]We use C++ for a ton of low-level stuff at work, most of it without the use of [font="Courier New"]virtual[/font], etc...[/size]
[size="2"]It's very "C-like" in style, but we've got a few extra tools to play with as well.[/size]
[size="2"]e.g. you said before that in C++ you give up control over when constructors/destructors are called, but in some very low-level code, we use placement-new ([font="Courier New"]new (preAllocatedBuffer) T()[/font]) and explicit destructor calls ([font="Courier New"]p->~T()[/font]) to do exactly that. A lot of our low-level code [i]does[/i][/size][size="2"] take the reigns and make explicit decisions about all the things you say that you're giving up in C++.[/size]
[size="2"]Again, your idea of what "[url="http://macton.smugmug.com/gallery/8936708_T6zQX#593426709_ZX4pZ"]typical C++[/url]" is, is not at all what good C++ programmers write, and yes, it is C++, not C.[/size]

[size="2"]You really are just saying that shit C++ is worse than good C code... or that because you've got extra tools in your tool-box, you're somehow [i]forced [/i]to use them for every problem? What's wrong with having a few extra tools that you only pull out occasionally?[/size]
1

##### Share on other sites
[quote name='Conoktra' timestamp='1311037849' post='4837125']C is used in Linux because of its versatility. It can do everything C++ can do, but also gives you fined-tuned control over [i]every[/i] aspect of program at the cost of expertise.[/quote]

Brainfuck can do everything C can do, but also gives you fine-tuned control over every atomic instruction of your program at the cost of expertise. So why isn't Linux written in Brainfuck?

[i]Because your argument is fallacious. C does not do everything C++ does, in the sense that C does not provide black-box abstractions that C++ provides. This means that C is fundamentally a less productive language when comparing the efficiency of experts in either language.[/i]

[quote]In C++ it has built-in OO functionality via classes. This makes things such as polymorphism much easier. BUT, by using classes you loose control of how object pointers are handled in the member functions, you loose control of how constructors/destructors are called and when, how member-function pointers are stored within the structure, etc etc.[/quote]

Bullshit.

Object pointers are always passed using thiscall, i.e. in the first (hidden) parameter to the function. This is not magic and does not remove anything. In fact, you can write a shim that translates from arbitrary C-style pointer passing to thiscall in three lines of code:

[code]int InvokeFoo(int argument1, int argument2, Foo* ptr)
{
ptr->FooImpl(argument1, argument2);
}[/code]

Construction and destruction are performed in very, very well defined ways and in well defined orders. I suspect you don't know C++ very well if you aren't aware of how to reason about construction and destruction order in a program. For stack-allocated objects it's bloody trivial; for free-store-allocated objects, it's only slightly more complex due to the presence of placement new and explicit destructor invocation. But those things give you perfect control over construction and destruction patterns, not remove control.

Your last "argument" is nonsensical. My best guess is that this is some oblique reference to the vtable, which is hardly something you need to tamper with... well, ever, really, in a proper C++ program. And guess what? If you're psycho and really want to write your own vtable, C++ has member function pointer syntax which lets you do so, while retaining thiscall semantics.

So there is absolutely no loss of control here - you just personally don't know how to obtain that control, apparently.

[quote]In C you can still do OO programming just like C++ classes by using structures and callbacks via function pointers. But, you get the ultimate control of how everything happens. YOU decide when constructors/destructors are called, YOU decide how object pointers are passed to member-functions, and YOU decide how member function pointers are stored in the structure and how they are alligned.[/quote]

I control all of that in C++ too. And, what's better, I don't have to think about it 99% of the time, because the language makes certain default guarantees that accommodate the [b]vast[/b] majority of use cases with no issue.

And, guess what, I can still control all of that by hand if I really want to.

[quote]I could choose a special alignment of the function pointers that could produce huge performance boosts on certain systems.[/quote]

AFAIK any decent C++ compiler will native-align the vtable entries; in fact, they'd almost have to bend over backwards [i]not[/i] to, because vtable entries are by definition machine pointer size to begin with. So I don't think you really know what you're comparing with here.

[quote]I could choose to pass the object pointer to the member function via the stack instead of a register, thus saving the register for a different parameter--which when used right, could provide huge performance boosts.[/quote]

You can implement your own calling convention on top of thiscall trivially in C++, if you know what you're doing. (Compiler specific, of course.)

[quote]In C++ I am writing a bubble-sorting algorithm to sort a list of strings alphabetically. In C++ the quick'n'easy solution would be to store the strings as a std::string, and store the group of strings as a std::vector. Once sorting, each time I swap two strings a total of 10 things happen: A) a extra std::string constructor is called, and B) 3 memory-free operations, and C) 3 memory-alloc operations, and 3 memory-copy operations. If each string is 10,000 characters in size, and there are 40,000 strings your looking at a huge overhead implementing it this way.[/quote]

Or you could use std::string::swap like any first-year C++ student would know to do, and your C++ program boils down to the C program with extra type safety and memory management safety.

The vast majority of people who argue against C++ are not arguing against the language itself, but rather against people who don't know how to use the language. Granted, crappy C++ "programmers" are a dime a dozen, so caveat emptor - but there's no really good argument for C over C++ that actually has to do with the languages themselves. It's all down to comparing tools, relying on history, selecting for certain types of programmers, or bigotry.
1

##### Share on other sites
[quote name='Hodgman' timestamp='1311046037' post='4837172']
[size="2"]You really are just saying that shit C++ is worse than good C code... or that because you've got extra tools in your tool-box, you're somehow [i]forced [/i]to use them for every problem? What's wrong with having a few extra tools that you only pull out occasionally?[/size]
[/quote]
So what would your conclusion be for both C and C++? A right tool for the right task? What tasks are right for C but not C++? What tasks are right for C++ and not C?

I'm still waiting for better code examples. Indeed, I am a shit C++ programmer, which doesn't take away the fact that C++ is bloaty when compared to C(yes, I provoke on purpose. Prove me wrong ). And this is one of the reasons why C is preferred over C++ in many low level tasks.

-1

##### Share on other sites
[quote name='Calmatory' timestamp='1311047148' post='4837187']
Indeed, I am a shit C++ programmer
[/quote]
And that is exactly the problem. How can you make statements about a language you admittedly have low expertise in ? The "benchmark" example you provided earlier is the perfect example of that. You were comparing two pieces of code that had nothing at all in common except for outputing text. That text editor written in C is faster than that protein-folding simulator written in C++. Obviously this must mean that C is better...

In fact this is something one can observe in almost all C vs C++ discussions. The most rabid C defenders (explicitly including Linus Torvalds) have actually very little knowledge of C++, sometimes leading to almost absurd arguments.

[quote name='Calmatory' timestamp='1311047148' post='4837187']
which doesn't take away the fact that C++ is bloaty when compared to C(yes, I provoke on purpose. Prove me wrong )
[/quote]
Prove yourself wrong by learning C++ before making factually incorrect statements about it. Counter arguments to your points are trivial common knowledge to every semi-competent C++ programmer.
2

##### Share on other sites
I did not base my judgment solely on the code I provided and it's size, but also on my past experience with it for writing [b]small[/b] code(binary size sub 1024/4096 bytes) and the common practices in demoscene. Instead of ranting, teach me. Show me. I'm asking for fourth time for someone to show how things are done correctly, and I'd appreciate it if it actually showed that C++ would be more ideal for low-level development than C. Especially for embedded systems in which resources are scarce.

I'm waiting for code.

0

##### Share on other sites
[img]http://chzmemebase.files.wordpress.com/2010/12/memes-you-code-in-c-son-i-am-disappoint.jpg[/img]

*Note: I code in C++. I just thought I'd share the laughs.
1

##### Share on other sites
[quote name='Calmatory' timestamp='1311047148' post='4837187']
So what would your conclusion be for both C and C++? A right tool for the right task? What tasks are right for C but not C++? What tasks are right for C++ and not C?[/quote]IM[b]H[/b]O, C is the right choice if:
* you've got a team who are good C programmers but bad at C++, and you don't trust them to not write bad C++ code ([url="http://research.scee.net/files/presentations/gcapaustralia09/Pitfalls_of_Object_Oriented_Programming_GCAP_09.pdf"]pitfalls of OOP[/url] is a good example of the performance disaster that bad C++ can lead to).
* you're on a platform with a good C compiler but a bad C++ compiler.
* you really want to use some C99 feature that isn't supported in your C++ compiler for some reason.

[quote name='Calmatory' timestamp='1311051634' post='4837209']Instead of ranting, teach me. Show me. I'm asking for fourth time for someone to show how things are done correctly, and I'd appreciate it if it actually showed that C++ would be more ideal for low-level development than C. Especially for embedded systems in which resources are scarce. I'm waiting for code.[/quote]I'm not going to fix your hello-world [font="Courier New"]printf[/font] vs [font="Courier New"]cout[/font] loop, because it's just a hello-world problem and has no relation to any real problems. Does anyone even use [font="Courier New"]cout[/font], in anything but "hello world"? [img]http://public.gamedev.net/public/style_emoticons/default/tongue.gif[/img]

I'm too busy writing high-performance C++ for embedded devices to tutor you right now, but the folks at Dice have some presentations on how to not write bad C++ that you should absolutely read: [url="http://publications.dice.se/publications.asp?show_category=yes&which_category=Engineering"]http://publications....ory=Engineering[/url]. The 'Scope Stack Allocation' presentation is a particularly good gem.
You'll be happy to see that the result is very "C style" [img]http://public.gamedev.net/public/style_emoticons/default/wink.gif[/img]
1

##### Share on other sites
Indeed, the presentations are real gems! Thanks for them, very interesting. I guess it's needless to say at this point that to make the most out of C++, it has to be very C-like, perhaps even relying on the C library rather than the C++ one?
1