4 ms·
> ICU has almost everything about their APIs correct No. If you have a common function with 13 arguments: U_CAPI void ucnv_convertEx( UConverter *targetC
by svnpenn 4y ago
> ICU has almost everything about their APIs correct
No. If you have a common function with 13 arguments:
U_CAPI void ucnv_convertEx(
UConverter *targetCnv, UConverter *sourceCnv, // converters describing the encodings
char **target, const char *targetLimit, // destination
const char **source, const char *sourceLimit, // source data
UChar *pivotStart, UChar **pivotSource, UChar **pivotTarget, const UChar *pivotLimit, // pivot
UBool reset, UBool flush, UErrorCode *pErrorCode); // error code out-parameter
you've done something terribly wrong. Refactor it as a method, do "factory" stuff, something. Just shocking what C people are able to put up with.
- the_svd_doctor 4y agoA method in C?
- TillE 4y agoIt's an extremely common pattern in C to have a struct with a bunch of associated functions which behave exactly like methods, taking a pointer to the struct as their first argument.
- eyelidlessness 4y agoYup. This is exactly how Python methods work too. And how most functional code looks in any language.
- olliej 4y agoobject oriented code does not mean "language support for object oriented code". Object oriented code is very common in many larger C libraries (or things like the linux kernel). The only difference is that you don't get any compiler support to prevent errors. The standard model is: typedef struct __MyType MyTypeRef; struct MyTypeMethodTable { // the destructor - honestly at an api level you should probably have retain/release instead void (destroy)(MyTypeRef _this); // void (someMethod)(MyTypeRef _this, int someArg); }; struct __MyType { MyTypeMethodTable vtable; }; void MyType_destroy(MyTypeRef _this) { _this->vtable->destroy(_this); } void MyType_someMethod(MyTypeRef _this, int someArg) { _this->vtable->someMethod(_this, someArg); } Then an actual type is implemented as struct MyConcreteType { struct __MyType base; // some fields }; void MyConcreteType_destroy(MyTypeRef value) { MyConcreteType realValue = (MyConcreteType )value; // cleanup anything you need to do free(realValue); } void MyConcreteType_someMethod(MyTypeRef value, int someArg) { printf("I: %d\n", someArg); } MyTypeMethodTable MyConcreteType_MethodTable { .destroy = MyConcreteType_destroy, .someMethod = MyConcreteType_someMethod }; MyTypeRef CreateMyConcreteType() { MyConcreteType *result = calloc(1, sizeof(MyConcreteType)); result->base.vtable = & MyConcreteType_MethodTable; result->someField = whatever; return &result->base; // Or similar. avoid UB in C can make this weird } You can see that trivially this is pretty much what notionally OO languages like C++, Java, Haskell, etc do. An apparently not-uncommon error that happens in COM is: someObject->whateverTheirMethodTableIsCalled->someMethod(theWrongObject) Possibly with someObject and theWrongObject the other way around. The end result is sadness either way. OO programming is super effective for many things, and generally better for a lot of design, especially for libraries and frameworks. But nothing about OO requires compiler/language support - indeed the first discussions of OO code predated OO languages - but it's hopefully obvious to see that if the compiler can manage this, it results in less code, and less opportunity for error, and because the compiler manages those semantics it should technically produce better code (e.g if the compiler knows that "vtable" is actually a vtable pointer, it knows there is no circumstance it can change[1]). [1] Yes you could have incorrect code through UB, but in that case the compiler is allowed to do the "wrong" thing.
- eyelidlessness 4y agoThat’s downright readable compared to some type defs I encounter for JS libraries! (No really it’s awful I agree.)
- thrown_22 4y agoYou call that horrible, I call it clear and concise. There's no hidden magic that will come back and bite you when it changes in another part of the code base.
- oplaadpunt 4y agoYes, for a C function I agree. With just a glance, I can see what this function will do, and what the arguments generally mean. It could be cleaned up a bit (source as a single argument) but this is more about data design, which you might have little control over.
- pjmlp 4y agoWelcome to Win32, Xlib, Motif, OpenGL, Vulkan,...
- db48x 4y agoThat’s just an aesthetic choice, and C doesn’t give you many options. Structs for the pointer and limit might be nice, but that wouldn’t be a huge improvement. The truth is that this isn’t really a “common” function; most programs will call it (or one of its siblings) just a few times for input and output. You’re write that code once and then never worry about it again. Leaving all that aside, if you think you can do better, feel free to write a wrapper library and prove it.