13 ms·
C99/C11 dynamic array that mimics C++'s std::vector
- tines 10y ago> What if we want to be able to store more than integers in our dynamic array ? [...] The array_push_back function also needs to be refactored in order to account for the size and type of what we store in the data buffer. A possible approach is to use a macro, instead of the original function: This is a situation where C++ really shines: you can use C++ with templates and not only have much cleaner code, but also avoid forcing the compiler to inline every single call to array_push_back. And you don't even need to use anything more than structs and functions, you don't need to go "full OO". I wish C programmers would be more open to C++ but it seems like they're for the most part pretty closed. That's probably the C++ community's fault, but I'm not sure how to make amends.
- noselasd 10y agoIn that regards, I also think C++ programmers should be more open to C programmers that just needs a few new language constructs. It is not an all or nothing approach. It's fine to write code that looks and feels mostly like C, but still takes advantage of a few nice C++ features.
- tines 10y agoI agree, which is why I mentioned that you can do what this article suggests without using anything except functions and structs. No classes or class features required.
- blub 10y agoThere are perfectly legitimate use-cases for such a "C+" style. The disadvantage is that it won't be considered "good C++ style" and would limit the attractiveness of the project to C++ programmers that enjoy modern C++. If one would include vector, string, array, smart pointers and maybe simple template code in C+ it would already have a big safety advantage over plain C.
- semi-extrinsic 10y ago> would limit the attractiveness of the project to C++ programmers that enjoy modern C++ As Linus wrote when someone told him on the mailing lists that Git should've be done in C++ """ Quite frankly, even if the choice of C were to do nothing but keep the C++ programmers out, that in itself would be a huge reason to use C. """
- pjmlp 10y agoYet he had to move to Qt for Substrate due to issues with Gtk.
- inDigiNeous 10y agoWell, Linus is a great C -programmer, so that's something he knows well and likes to protect and embrace.
- JoeAltmaier 10y agoSounds like a bitter old fossil. My first look at Linux, there were 200 sound-card drivers in the source base. Mostly identical except for the layout of bits in the control register. Any change to the api or conventions, 200 files had to be edited. An ideal place to refactor as a 'sound card' base class and derive classes to handle the (miniscule) differences. "Keeping new ideas away" is a good working definition of an old coot.
- st3fan 10y agoYou don't necessarily need C++ to implement a good driver design with base classes and minimal code duplication. It is perfectly possible in C.
- deleted 10y ago[deleted]
- Vindicis 10y agoThat's pretty much how I use C++. Granted, my knowledge of it is very minimal. I was forced into C++ because of my Broker's api so... One of my largest gripes making the switch, is the OO paradigm. I get it, I understand it, and I really want to love it. But the theory of it vs. the implementations that I've seen, ugh. And the number of ways you can initialize variables etc... just makes no sense to me. It's like they just keep adding new ways to do things, for no other reason than because they can. What's wrong with only have one way to do simple things like that? I don't know, maybe it's just me but I strive to keep my code as simple, concise as I possibly can. I have enough complexity to deal with solving problems than having to wrestle with my language on top of it. Just to note, I'm strictly talking about having to use other people's code merged with my own. If it was solely me writing, I'd just use C++ for some of the nice things built in and toss classes and all that into the fire. YMMV.
- jjawssd 10y agoWe need a book with modern best practices like what happened in the Javascript world with "JavaScript: The Good Parts" from which we can glean a clean effective dialect, leading to a new C++ uptake and renaissance similar to what happened in the Javascript world in 2008
- swift 10y agoThey're not really equivalent, but Scott Meyers' series of books on C++ are some of my favorite technical writing ever, and his most recent book "Effective Modern C++" should be on the shelf of every working C++ programmer.
- 72deluxe 10y agoI agree with this - I finished reading this just before last year and I cannot recommend this book enough. It really is excellent (including topics like reference collapsing, type deduction in different scenarios, idiosyncrasies of async tasks). A great book and essential.
- kgabis 10y ago
- pjmlp 10y agoThe thing is, we get dispointed when the code just looks like "C compiled with C++ compiler", with all security exploits that entails. A good example is how Turbo Vision, Object Windows Library, Visual Components Library, Qt look like and the concessions Microsoft had to make to Afx so that it got rebranded into MFC and appealed to the Windows C developers.
- beojan 10y agoThe idiosyncrasies in Qt etc. are because they were first designed when C++ was very young, and many compilers had poor support for the STL.
- pjmlp 10y agoYou misunderstood me. I was first talking about the set of frameworks that dared to embrace the then C++ best practices. All of them are older than C++98, or even before STL was being considered. Then I mentioned the fact that when Microsoft presented their Afx framework to their beta testers, mostly Windows C developers, it was considered too high level and it was rebuilt as MFC.
- 1wd 10y agoThe term "C with classes" is almost used as an insult in some circles. I wonder if "C++ without classes" would be a better idea.
- CyberDildonics 10y agoC++ without polymorphism or inheritance is how the people that I think are worth listening to seem to do things.
- 72deluxe 10y agoHow would that be a good idea? I don't understand how I could write effective software without polymorphism or inheritance, other than in tiny tiny applications.
- psyc 10y agoInheritance is syntactic sugar for composition. But, I think of it as reverse-SS, because I find composition much more readable.
- sidlls 10y agoI've written entire components for networked and multiplayer modes for games, and an algorithmic trading system that uses ML in C++ using just structs, unions, templates, functions and composition (no polymorphism or inheritance).
- CyberDildonics 10y agoThink about this: what does it give you besides generic execution and data when the size of the data varies? Templates already let you build generic data structures and algorithms when the size of the data on doesn't vary.
- gpderetta 10y agowithout runtime polymorphism. /pedantic++
- CyberDildonics 10y agoIt's always just one or two more features and before you know it - C++
- WalterBright 10y agoThe thing is, everyone has a different idea of what those one or two features should be! Even me, https://digitalmars.com/articles/b44.html https://digitalmars.com/articles/b44.html
- cefstat 10y agoYes, despite all the problems in C++ it's just a much better language than C for defining new types that are on par with "native types". std::vector has so many benefits over any possible C implementation that there is no real comparison. Genericity is one, no need to remember to free after use is another. By the way, why is this C implementation something that requires C99 or C11 features? It looked just like standard old-fashioned ANSI C to me.
- pskocik 10y agoI like my C alternative better. I pass growth factors/increments as parameters to the vector macros so that I can affect how it grows on capacity exhaustion during each call and I have macros for creating and closing uninitialized gaps. C++ loses on many potential optimizations by insisting that its types always be in fully well-defined states except inside methods. Moreover the particular GNU implementation of the STL on Linux completely fails to turn certain vectors methods into memsets, memcpies and memmoves where it could, which pesimizes those particular ops by like two decimal orders of magnitude. The insistence on using new/delete based allocators instead of reallocs is a significant pessimization too. Reallocs perform, on average, several tens of percents better than new allocs followed by copies. An even more noticable pessimization is in the compilation times. Including `<vector>` adds good chunks of a second to the build time of an average-length translation unit. In comparison, working with C, even with all my generics included, on top of a good build system makes me feel as if I was working with a scripting language. No lags.
- tines 10y agoYour criticisms of vector are well put, but if anything they're all really problems with a particular implementation of the standard library, not the C++ language. If you make your own libraries you're free to do whatever you want, and template metaprogramming can help you gain even more performance by easily using optimized implementations for specific types, etc.
- swift 10y agoThese are fair points and issues like these are why many larger projects have their own custom vector-like classes. One thing I did want to call attention to is your point that C++ best practices require that objects be in well-defined states except inside methods. That's true, although it's not a language requirement, but in many cases you can get the best of both worlds through the use of lambdas. A simple example to give the flavor: template <typename Function> void my_vector::munge(Function initialize_gap) { create_uninitialized_gap(); // Some of our elements now contain uninitialized memory. // We don't want to expose this to arbitrary code, but // it's OK to expose this to the function the user // provided specifically to handle this. initialize_gap(gap_begin(), gap_end()); // The gap is now initialized, so when we return from // this method, we'll be in a well-defined state. } This is the standard pattern you use for such things in functional languages, and it works very well. The idea is to ensure that intermediate states are only seen by code that explicitly expects to handle them.
- necessity 10y agoMore like the clusterfuck that the language is than the community.
- pskocik 10y agoIn my codebase, I use expression macros (with a little bit of __typeof__-based type checks) to do it fully generically. Much of what you think you need C++ for can be done almost just as succinctly on top of plain C (I'm talking automated scope cleanups, semi-automated error handling, many generic things) and the compile times fly. I agree C++ has had some good ideas. In fact, I originally wanted to use it. But I came to the conclusion that it's too bloated, and more importantly, fundamentally broken in certain ways (RAII, exceptions, even templates and namespaces), and I ended up emulating what I think the good parts of C++ are on top of plain C. When C++ programmers think C, they usually think lack of generics and lots of explicit manual, micromanagement and pointer arithmetic, but it can be a lot more than that.
- tines 10y agoI'm not familiar with this approach, but I think it would entail code size increase for each use of the macros, and a runtime overhead for the typeof checks, correct? If so, C++ has a nice advantage in that all the type checks are at compile-time so you don't pay any more for those, and also you don't duplicate binary code with macros.
- pskocik 10y ago__typeof_ is a compile-time feature. It's like C++'s decltype. (You can make C++'s auto out of it too.) There's is a potential for code size increases with this approach, that's true. But the potential also exists with inlinable vector methods.
- tines 10y agoYes, but for the vector methods, you leave that decision up to the compiler which should be able to take the best route.
- pskocik 10y agoTrue. However, I think in the case of the dynamic array, this effect is minor. The macros are small, and most of the time, they end up wrapped in a function anyway. But, admittedly,it doesn't scale well to very big generics, which should be always wrapped in a function. (Eventually I plan to shove a simple transpiler in front of all my C code and do this, along with namespacing, how I think it should be done.)
- throwaway89121 10y agoI want something like std::vector, but I don't want exceptions. How does C++ help in this case?
- ensiferum 10y agostd::vector is a container in a c++ standard library. C++ is a language. You're mixing apples and oranges. What I'm saying is that you can use C++ without having to use STL and you can use C++ just fine to implement your own exceptionless container if you want (and many such implementations exist already). And you can also compile with exceptions disabled. Should your code throw it will terminate the process.
- gwmarks 10y agoSo, garbage like "And you can also compile with exceptions disabled. Should your code throw it will terminate the process." is in the plus, and the parent comment is downvoted. The C++ community at its finest.
- tines 10y agoIt will help you either use std::vector with a custom allocator that does not throw exceptions, or else write your own non-throwing vector class with templates, which would, I hope, be more maintainable and easier to use correctly than an equivalent written in C.
- hendzen 10y agoCompile with -fno-exceptions
- 72deluxe 10y agoYou can use any part of C++ without having to use the rest of it. For example, you don't have to write your own std::allocator to use std::vector, nor do you have to use exceptions. You can use which bits of C++ that you want - that's its great strength, permitting low-level behaviour whilst supporting high level abstractions (and as simple or as complex as you like).
- jokoon 10y agoA solution might be a minimal subset of C++, so with STL containers, some syntactic sugar, easier and faster to compile (modules), etc. Basically what go and rust have achieved, except you keep most of the C/C++ syntax "taste", and you remove too high level stuff like templates and inheritance. Honestly I don't think templates are so useful, since most programmers already re-use STL containers, which are templates, but nobody really write relevant template classes. > That's probably the C++ community's fault The problem with C++ is backward compatibility with C and big corporations trying to not break their codebase. It makes it very hard to make the language evolve. For example D is a very good language, but it can't gain momentum as long as it doesn't have a real way to gain presence. We just have to wait that corporation clean their codebase to make room for C++ compilers to adapt, and then things should improve.
- tines 10y agoYou can't have the STL containers without templates though. If you want to special-case them in the compiler, then what about the algorithms library? If you want to keep the algorithms library, then what about your own code that implements a higher-level algorithm using the lower-level ones in the algorithms library? Without templates you have to write your algorithm once for every type of container, or else use indirect function calls which would not be good. So this won't really work well I think. For me, the only selling point of C++ is templates, and I've never written a nontrivial program that didn't use them (and I like to think that I don't just use them frivolously).
- sgeisenh 10y agoParametric polymorphism is a powerful abstraction. Makes for much more readable programs than ad-hoc polymorphism a la vanilla C.
- deleted 10y ago[deleted]
- 72deluxe 10y agoYou do realise that STL containers fundamentally rely on templates, right? We use loads of template classes at where I work. Our code would not be maintainable without them.
- mtdewcmu 10y agoThe chief benefits to pure C IMO are that it's more portable, compiles faster, and it's a simple and stable language. Writing C takes more effort in some cases, so it's a trade-off.
- maxlybbert 10y agoIt's goofy, but one of the reasons I'll pick C for a quick program is that both Windows and Linux will give better error messages for standard functions that fail and set errno than for equivalent C++ code that throws an exception. This isn't a question about the language, it's definitely a quality-of-implementation issue for standard exceptions to have what() give a useful message. Just to clarify perror will generally give a better error message in: FILE* fh = fopen("foo.txt", "w+"); if (!fh) { perror("unable to open foo.txt"); return 1; } Than what() will in: std::fstream fs; fs.exceptions(std::ifstream::failbit | std::ifstream::badbit); try { fs.open("too.txt"); } catch (const std::exception& ex) { std::cerr << "unable to open foo.txt: " << ex.what() << '\n'; } (Ignoring other issues such as whether exceptions are better than return codes, etc.)
- mtdewcmu 10y agoA lot of people seem to use stdio even in C++. Having comprehensible error messages is related to being a simple language, IMO. C++ compile-time errors are notorious for being unreadable. I'm not familiar with the output of what(), but I'd wager that the problem is a consequence of all the abstraction.
- rkv 10y ago>This is a situation where C++ really shines I agree but passing in the size is really not much of a deterrent.
- AlexeyBrin 10y ago> you can use C++ with templates and not only have much cleaner code, but also avoid forcing the compiler to inline every single call to array_push_back Actually, you really, really, really want to inline every call to push_back for std::vector. Here is a relevant presentation by Chandler Carruth https://www.youtube.com/watch?v=s4wnuiCwTGU https://www.youtube.com/watch?v=s4wnuiCwTGU . But, hey, you don't want to make the compiler work for you :).
- aceperry 10y agoI gave up on C++ right after I learned it. At the time, this was before the STL was part of the standard, it looked really cool but I could tell that it was easy to get into trouble and it would take years and years to be really good at it. When Java became popular, it looked like it solved a lot of the problems that I was afraid of with C++. But it was slow and really verbose compared to C++ and C. Recently, I heard someone say that today's C++ is not your grandfather's C++ and the speaker made some interesting comments about how much better it was. So I'm intrigued by it and definitely open to looking into the new C++.
- Joky 10y agoYep, worth giving another look: I couldn't stand C++ before C++11, and now I can't stand C anymore :)
- jagger11 10y agoWith the use of defer http://pastebin.com/EXZuRAdT http://pastebin.com/EXZuRAdT you could create it w/o the need to use array_free.
- kzrdude 10y agoThe dynamic memory allocation of the array's fields itself does not mimic std::vector, it's an extra indirection that C++ does not pay for. You can make it a non-opaque struct in C and copy it around.
- tines 10y agostd::vector does indeed have at least one pointer member (otherwise you couldn't have a std::vector with automatic storage duration because the size would be unknowable) so there is some indirection. Maybe you're thinking of std::array?
- kr7 10y agokzrdude is referring to the allocation of the Array struct on the heap. It should be something like this instead: Array array_create(size_t size, size_t sizeof_data) { Array result; result.size = size; result.capacity = size; if(size) { result.data = malloc(size * sizeof_data); } else { result.data = NULL; } return result; }
- oconnor663 10y agoI think kzrdude is actually referring to the void* pointers that the individual elements of the array live behind. Each of those requires an allocation to insert them, and an extra pointer traversal to read them. In C++ they would live side by side instead, as in regular C arrays. In C you'd need the struct to be redefined for each element type (maybe with another macro) if you wanted the same efficiency.
- kr7 10y agoI'm not seeing that? In ARRAY_PUSH_BACK, it just inserts the element directly into the buffer, provided there is enough capacity. There is no separate allocation for an element. You don't need to redefine the struct for each element type, since the macro casts 'data' (type void *) to whatever the array's type is.
- im3w1l 10y agoProbably more efficient to store the data array inline, with a flexible array member. That way creating takes only one malloc, and destruction only one free.
- krylon 10y agoThose are great. I haven't used C a lot in a long time, but I remember back when I wrote C code for a living that I ran into the exact situation flexible array members are made for around the time I also learned of them. "That will solve my problem elegantly", I thought, but unfortunately, the compiler we used only understood C89, so my hands were tied.
- userbinator 10y agobut unfortunately, the compiler we used only understood C89, so my hands were tied. You can do it in C89 too, just allocate sizeof(header) + n * sizeof(element).
- krylon 10y agoYes, but it's not the same. :( { It is fun, though - I once did it to write/read a data structure with two levels of indirection (i.e. an array of arrays) - Slurp the whole thing to memory, then adjust the pointers, and voila. } In C89 it's trickery and a little bit of black magic (at least a whiff of it), while in C99, it is an officially supported convention. On x86, where the C code I wrote ran, it wouldn't make much of a difference, but allocating header + array manually also means - if the code needs to run across a variety of CPU architectures - that one needs to look at alignment issues.
- makapuf 10y agoIIRC You don't have to play with pointers if you use the null array trick (put an array of size zero at the end of your struct and use it anyway)
- AlexeyBrin 10y ago
- to3m 10y agoThis sort of approach is a pain to use, because you keep having to cast when you're in the debugger, and there's zero type safety. And I'm afraid I don't have much positive to say about something like "((Vector2i * )arr3->data)[0].x = 333". You can do better than this! What is an array? It's 3 variables: base, length and capacity. So why not decide that an array is just that. 3 variables of the right size and type. #define ARRAY(T,S) T S;size_t S##_length;size_t S##_capacity Then you can make one like this: ARRAY(int,xs); You'll also need to initialise and these destroy array "objects". #define ARRAY_INIT(S) \ do { \ S=NULL; \ (S##_length)=0; \ (S##_capacity)=0; \ } while(0) #define ARRAY_DESTROY(S) \ do { \ Array_Free(S); \ ARRAY_INIT(S); \ } while(0) Add you'll probably want to add an item to an array too. #define ARRAY_ADD(S,X) \ do { \ if((S##_length)>=(S##_capacity)) { \ S=Array_Grow(S, \ sizeof *S, \ &(S##_length), \ &(S##_capacity)); \ S[S##_length++]=(X); \ } while(0) So you might use them like this: ARRAY(int,xs); ARRAY_INIT(xs); for(int i=0;i<100;++i) ARRAY_ADD(xs,i); ARRAY_DESTROY(xs); Array_Free is very simple, and Array_Grow is barely more complicated (however I wrote it off the cuff, so of course it could still be wrong). Both of these mainly exist just to keep stdlib.h out of the header. void Array_Free(void *p) { free(p); } void *Array_Grow(void *base,size_t stride,size_t *length,size_t *capacity) { *capacity+=*capacity/2; *capacity=MAX(*capacity,MAX(MIN_CAPACITY,*length)); return realloc(base,*capacity*stride); } Array accesses and iteration and the like are just done in the traditional way: for(size_t i=0;i<xs_length;++i) { printf("%d\n",xs[i]); } Even performs nicely with -O0. For a full implementation you'll probably also need a way of generating a static array. (I mainly found myself needing this for test code, which uses globals for convenience; most arrays I create normally are locals, or parts of structs.) You'll also need a parameters list for use in a function declaration or definition, and a macro that expands to all 3 variables. #define ARRAY_PARAMS(T,S) T *S,size_t S##_length,size_t S##_capacity #define ARRAY_ARG(S) S,S##_length,S##_capacity Like then you might have a function that takes a pointer to an "array": void FunctionThatTakesAnArray(ARRAY_PARAMS(T,*p)); And you call it like this: ARRAY(T,myarray); FunctionThatTakesAnArray(ARRAY_ARG(&myarray)); (I found this cropped up often enough that I needed the macro, but it was less common than I thought.) There's more you can do, but the above is the long and the short of it. This might all look terrible - or perhaps it sort of looks OK, but you're just not sure that it would actually work - but I've used this in a prototype project and thought it worked out well. (I've been using C for 20+ years, so hopefully even if I've got no taste, I've at least got a rough feel for what works out OK and what's going to end up a disaster.)
- piker 10y agoIs there a simple way to write the ARRAY_PUSH_BACK without a macro?
- AlexeyBrin 10y agoProbably, if you use memcpy and store the size of the data type as an extra parameter in the containing struct.
- inlined 10y agoI was thinking about that but I don't know what to do with the type of the param you're accepting. Could you do a const void* ref? Might just be easier to make it variadic.
- AlexeyBrin 10y agoSee this comment https://news.ycombinator.com/item?id=13346432 https://news.ycombinator.com/item?id=13346432 . At this point creating a function like: array_push_back(int nr_arguments, ...); that will replace the original macro ~~should be possible~~. Actually it won't work without using non-standard compiler extensions like typeof.
- inlined 10y agoIf you have a pointer-only array you could just make it based on void*. Primitives and by-value arrays make it a lot harder.
- krylon 10y agoI wrote pretty much this exact implementation of a dynamic array once. For several data types. And I had that same idea, let's use macro to fake generic programming. But while I admire the trickery some people pull off using the C preprocessor, I admire them from afar. My coworkers would not have let me get away with that, anyway. I am not a C++ programmer, but templates are immensely powerful, and after learning about them (a little, at least), I found statically typed languages without some form of type-generic programming to be very bothersome. Looking at C++ as "C with Templates" instead of "C with Classes" gives a very different picture (plus, Classes and such are still around in case they are needed, anyway). Every other year or so, I try to get my C++ up to usable standards, but I do not need it for work (except for that one time about three years ago), so I eventually lose interest. Maybe approaching C++ as "C with Templates" is a more promising route.
- tines 10y ago> Maybe approaching C++ as "C with Templates" is a more promising route. Genius. I'm doing this from now on.
- akkartik 10y agoFor many years now I treat C++ as "C with destructors and the STL". Examples: 1. RAII: https://github.com/akkartik/mu/blob/61fb1da0b6/010vm.cc#L484 https://github.com/akkartik/mu/blob/61fb1da0b6/010vm.cc#L484 2. STL: https://github.com/akkartik/mu/blob/61fb1da0b6/020run.cc#L50 https://github.com/akkartik/mu/blob/61fb1da0b6/020run.cc#L50 I really don't use anything else.
- krylon 10y agoOh yes, don't forget RAII as I did! (The name is super-awkward, but the concept is mind-blowingly awesome when it fully sinks in.) Other languages have begun to pick up on this, think of Python's with-Statements, and C#'s using (x = SomeClass())-blocks. but C++ still makes it easier to take advantage of this feature. Unless you play around with setjmp/longjmp. But to do that, you have to be ... special enough to not care about deterministic invocation of destructors in the first place. ;-)
- ensiferum 10y agoThe author could have just used glib.
- krylon 10y agoAlso, if one is not restricted because of license conditions, the Judy Array comes to mind: https://en.wikipedia.org/wiki/Judy_array https://en.wikipedia.org/wiki/Judy_array The API is very easy, and it's really fast.
- samfisher83 10y agoYou could add some function pointers to the struct initialize them and then you could do: Array a; a.add(&a, item).
- faragon 10y agoI wrote a similar thing for C99 [1], but "safe" (bound-checked), having both stack and heap allocation, and many other array/vector functions [2], including integer-optimized sort (in-place MSD binary radix sort -wich is availabe in typical C++ sort implementations, but not in C, as default qsort() relies on sorting functions-). With some benchmarks, too [3] [1] https://github.com/faragon/libsrt https://github.com/faragon/libsrt [2] https://faragon.github.io/svector.h.html https://faragon.github.io/svector.h.html [3] https://github.com/faragon/libsrt/blob/master/doc/benchmarks.md https://github.com/faragon/libsrt/blob/master/doc/benchmarks...
- wollstonecraft 10y agohttps://github.com/attractivechaos/klib/blob/master/kvec.h https://github.com/attractivechaos/klib/blob/master/kvec.h
- halayli 10y agofor cases where malloc checks complicate the example it doesn't hurt to use assert instead.
- antirez 10y agoAFAIK when the array is created, p->size should be set to 0, not to the size argument.
- AlexeyBrin 10y agoThis mimics C++ vector, e.g.: vector<int> vec(5); vec.push_back(11); vec.push_back(12); Now you have in vec: 0, 0, 0, 0, 0, 11, 12 But your suggestion is better from an API point of view.
- antirez 10y agoOh... thanks! I was not aware.
- antirez 10y agoPS please note that there is no memset nor any other mean to zero initialize the elements. So the code appears to have a bug anyway.
- AlexeyBrin 10y agoCorrect, this is a bug. calloc will better mimic the way C++ std::vector works.
- crossroads1112 10y agoI wrote something similar in C99 as well for another project. Initially, I used the same form used in this blog post however it's easy to see that the ergonomics for accessing the data are pretty terrible. I eventually moved to a solution where I prepended the capacity and size to the block returned to the caller and then wrote helper functions that accessed/modified these values. This way the caller can access values in the returned array just as they would one returned from malloc. The code (note, the `vec` type is just a typedef'd `void*`): https://github.com/crossroads1112/marcel/blob/master/src/ds/vec.c https://github.com/crossroads1112/marcel/blob/master/src/ds/...
- WalterBright 10y ago> A possible approach is to use a macro When macros start being used for metaprogramming in C, it's time to reconsider using C++.
- wott 10y agoWhen it's time to reconsider using C++, it's time to consider using something else :-)
- Ace17 10y agoLike the D programming language? :-)
- WalterBright 10y agoI do see people extending C++ using the preprocessor for more complex metaprogramming. Those should consider D.
- inlined 10y agoWhy does array_push_back need three parameters? Couldn't the macro just use size_of on the second param?
- AlexeyBrin 10y agoNot without some serious modifications, see line 6 of the macro: data_type *pp = arr->data;\ this is expanded to something like (when data_type is double): double *pp = arr->data; You can get rid of the third parameter. Just store the size of the data type in the container struct and use memcpy. Something like this (probably slower than the original): char *pp = arr->data;\ memcpy(pp + (size - 1) * arr->size_of_data, &(x), arr->size_of_data);\
- kev009 10y agoI would recommend David R. Hanson's "C Interfaces and Implementations: Techniques for Creating Reusable Software (Addison-Wesley Professional Computing Series, 1997, ISBN 0-201-49841-3). https://github.com/kev009/cii/blob/master/src/array.c https://github.com/kev009/cii/blob/master/src/array.c - this leaves resizing on the caller, but that could be retrofitted in. Most importantly is how the book explains everything.
- knorker 10y agoUgh. Multi-line macro. how about #define PUSH_BACK(a,x,t) push_back(a,&x,sizeof(t)) No multi-evaluation problems or other madness. Edit: Actually that won't work for expressions. So #define PUSH_BACK(a,x,t) do { t tmp = x; push_back(a,&tmp,sizeof t) } while(0) slightly better.