nothings / stb

stb single-file public domain libraries for C/C++
https://twitter.com/nothings
Other
26.57k stars 7.7k forks source link

stretchy_buffer.h doesn't compile as C++ msvc, any alternative other than passing the type? #178

Closed vexe closed 8 years ago

vexe commented 9 years ago

In particular it gives:

error 2440| '=' : cannot convert from 'void *' to 'float *' || Conversion from 'void*' to pointer to non-'void' requires an explicit cast

because stb__sbgrowf returns a void* and if I write, say:

int* ptr = null;
sb_push(ptr, 10);

the macro would expand trying to assign ptr to the return value of stb__sbgrowf - and as you know in C++ casts from void* must be explicit.

Is there any other way of working around this other than passing the array type to the macro?

nothings commented 9 years ago

Yeah, I don't know what that thing in the docs about supporting it in C++ is supposed to be. There are three ways I know of to implement it:

So once upon a time, stretchy_buffer.h was very explicitly "C only". Probably what happened is I noticed (or someone complained) that it didn't even compile in C++, so I added the '(int *)' cast to the realloc just so it would compile, nevermind the fact it actually doesn't work.

I'll update the documentation to be unambiguous that it's C-only. If you want something like this in C++, you can just use vector<>, roll your own, or use stb.h and turn off strict-aliasing optimizations.

vexe commented 9 years ago

Thanks! I learn a lot from your codes.

No I don't want to use any C++ stuff (stl, vector etc). I really liked the idea of allocating extra space for the size/capacity so I went with it. Currently I'm passing in a type parameter to the push/add/regrow macros to do the cast, not ideal, but not bad.

I noticed something, in your needgrow macro you check to see if 'count + increment >= capacity' is true, then you grow. So if we had an array of count=3 capacity=4, and we're pushing in an element, well 'count + increment == capacity' (3 + 1 == 4) but we don't need to grow cause there's still one element left till we reach our full capacity, which is fine we're just growing before we're completely full, but I just wanted to know if this was something intentional/by design, if so, why?

Another thing, a typo in stb_arr documentation: right at the beginning "assert(my_array[1] == 2.0f);" pretty sure that should be my_array[2].

Not related to this issue but since I have you here I figured I might as well ask: As a new comer to your libraries (stb.h in particular) (I'm fascinated by them). Where do you recommend I should start? I want to study it, understand it and basically be able to write my own. I was interested in the C introspection stuff but I saw it commented out, not sure why. Sometimes it's hard to understand some of the things like hashmap stuff, I saw some magic numbers and XORs etc.

Do you have anything in your libraries regarding serialization? I wrote my own reflection system that lets me set/get struct fields by name at runtime and do all sorts of funk and jazz. So I figured I could use it to write a serializer, but I got stuck at pointers, I wasn't sure how to handle them, since a pointer could be pointing to an object, or it could be a dynamic array, how does the serializer know which? should I add annotations to help it decide? even if it finds out its an array, how does know how long it is? should I do the extra 2 integers header trick for all my arrays?

Lastly, Is there a way in your library to get a FILE* to a block/region in memory? In Linux there's fmemopen but I can't seem to find an equivalent in Windows. Internet seems to suggest MapViewOfFile but I don't think that is it, I don't want a memory mapped file, just FILE* to a memory region. (My intention was to use this memory stream to serialize my stuff into and then when I'm done I write the whole stream to disk, that way I only hit IO once)

Thank you very much in advance. Sorry for the many questions. If there's a preferred way you'd rather receive questions via, please let me know.

vexe commented 8 years ago

I wrote a DynamicArray.h building on top of stretchy_buffer adding more features. https://github.com/vexe/DynamicArray.h was wondering if you could give me feedback on it. And sorry about that long post lol I got a bit too excited when you answered me (you're one of my heroes)

nothings commented 8 years ago

Sorry, I didn't intentionally not answer this, I just never got around to it.

DynamicArray:

Things like '#define ListLast(List) (List[_ListCount(List) - 1])' should be '#define ListLast(List) ((List)[_ListCount(List) - 1])', although it's pretty unlikely to be misparsed since usually List has to be a simple variable so you can e.g. push onto it; I'm not sure there are any scenarios where it could be misparsed, but why take chances?

I don't really understand 'Toggle' in ListForEach... is it just because you're using the inner for to get the 'auto' w/o having to deal with brackets? I don't know why the outer loop is testing Toggle and flipping its value, though. Hmm, maybe that makes 'break' work? You should probably document that part of the implementation since it's pretty subtle. Also, I prefer to make variables like that less likely to collide with user variables by picking a name that's more random/garbagey; for consistency you could at least name it Toggle. (But of course names starting with are reserved for the compiler, so you could get collisions or warnings on other platforms.)

I still think if you're in C++ you might as well use a template, instead of having to pass the type in to many of the macros. The implementation can still work the same way with the header integers, it just lets you make the API cleaner. Unless you have a seriously strict no-templates rule.

vexe commented 8 years ago

Thanks for the reply!

DynamicArray: 0- You're right, I forgot to do it in that instance. 1- That's pretty much what it does yes, make break work! You're right about the naming too. 2- I'm all into building my own libraries and tools. it gives me more control, I know exactly how they work, they do exactly what I want, if there's a missing feature I know how to add it, if there's a bug I know how to fix it. I can't say the same if I use a library. But if it's something that takes a lot to implement, or I don't know how to implement and I'm not interested in the theory behind it atm, or I've already implemented it a bunch of times, and there's already a 'good' (ehm ehm...) library for it, then yes I'll use that library. Which means under no condition I would use any of the STL stuff :D

Could you elaborate on the annotations a bit more? i.e. say I had an int*, what kind of annotations I'd need to use to tell the serializer how long this dynamic array is? do I tell it about the name of a count variable it could find in the same struct? Maybe another annotation is 'List' or 'Dynamic' if we're using DynamicArray/stretchy_buffer, in that case the serializer would use ListCount.

nothings commented 8 years ago

template != STL

Yeah, I meant an annotation like DYNAMIC_ARRAY or whatever.

vexe commented 8 years ago

Well yeah I know what you mean, but it's still bad :D

On 29 October 2015 at 02:13, Sean Barrett notifications@github.com wrote:

template != STL

Yeah, I meant an annotation like DYNAMIC_ARRAY or whatever.

— Reply to this email directly or view it on GitHub https://github.com/nothings/stb/issues/178#issuecomment-152089452.

vexe commented 8 years ago

I learned about decltype and it seems it could be used to remove the need to pass a type parameter in the List functions. What do you think about it: https://github.com/vexe/List.h/commit/325f3c3244420899de0e6f3a2207c4a31b21e47f