Back to Basics: C++ API Design - Jason Turner - CppCon 2022

  Рет қаралды 82,744

CppCon

CppCon

Жыл бұрын

cppcon.org/
---
Back to Basics: C++ API Design - Jason Turner - CppCon 2022
github.com/CppCon/CppCon2022
Let’s face it: writing a C++ API can be a daunting task. You recognize that APIs are a critical aspect of your code, and you’d like to provide your users with a great experience, but how?
This talk will focus on one key aspect: "Making APIs Hard to Use Wrong." How do we design APIs that help, instead of hurt, our users?
---
Jason Turner
Jason Turner is a regular speaker at C++ conferences, the creator of the C++ Best Practices book, several C++ related Puzzle Books, “Learning C++ Best Practices” video series from O’Reilly and the cppbestpractices.com online C++ coding standards document. As a contractor, speaker and trainer he has specialized in helping others produce high quality C++ code.
Jason is also host of the KZbin video series, C++ Weekly.
__
Videos Filmed & Edited by Bash Films: www.BashFilms.com
KZbin Channel Managed by Digital Medium Ltd events.digital-medium.co.uk
#cppcon #programming #api

Пікірлер: 99
@fredhair
@fredhair Жыл бұрын
Jason has probably done more for my understanding of good compile time safety than most, always full of good logical points and I generally learn a lot when he's speaking.
@jaybee9054
@jaybee9054 Жыл бұрын
Putting the fun back in CPP, for sure!
@zahash1045
@zahash1045 Жыл бұрын
Video starts at 3:46
@yangwei6761
@yangwei6761 Жыл бұрын
ty, swordsman.
@TheClonerx
@TheClonerx Жыл бұрын
Poor camera guy
@joestevenson5568
@joestevenson5568 Ай бұрын
He knew what he was in for when he found out it was a Jason Turner talk
@RayaneCTX
@RayaneCTX Жыл бұрын
The title is a bit misleading. I think the presentation is more appropriately named as "C++ best practices for writing public APIs" (appeal aside). However, taken for what it is, this is such a good presentation.
@cppevents
@cppevents Жыл бұрын
Always love hearing Jason's talks. 👍
@bryancoxwell5827
@bryancoxwell5827 9 ай бұрын
I do not know a single thing about C++ and still found this to be an excellent talk.
@jplflyer
@jplflyer Жыл бұрын
As always, a great talk, Jason. Thank you.
@_Omni
@_Omni Жыл бұрын
Jason is the best 👏
@seancpp
@seancpp Жыл бұрын
Oooh yes a new talk from Jason? Time to grab some popcorn
@alidanish6303
@alidanish6303 Жыл бұрын
I understand that Jason probably has lot to cover in the interactive session but since the discussion is about API design and error handling is fundamental part I just wish little more time where noexcept/ exception vs errno/ errorcodes best practices are discussed. Secondly, the person trying to explain error codes is somewhat convoluted, so we can have platform or lib specific error codes and then reaction in design can be based on the std::error_condition(platform independent).
@dyazmovies
@dyazmovies Жыл бұрын
Great presentation, very insightful. Promoting those books right before Christmas was a smart move :)
@itorrestp
@itorrestp 6 ай бұрын
Nice to see a conference with people interaction.
@MichaelLauerDr
@MichaelLauerDr 20 күн бұрын
Excellent talk. I learned something today.
@Dth091
@Dth091 Жыл бұрын
14:15 haven't checked the standards wording but both GCC and Clang trunk work like you'd expect with [[nodiscard]] on enum class/struct declarations.
@icestormfr
@icestormfr Ай бұрын
Enumerations were not in the original proposals/working drafts, but in the current/latest revision it is included. See also [[nodiscard]] on cppreference website and the P0189R1 (C++17 features, "Wording for [[nodiscard]] attribute.") where it says "class or enumeration". In revision R0 (P0189R0) neither class nor enumeration was mentioned (same for P0068R0)
@prateekpatil4845
@prateekpatil4845 Жыл бұрын
"Pit of Success" is fairly old but a very prominent talk with that as a theme was Scott Meyers talk "The most important design guideline"
@robertjmccabe
@robertjmccabe 11 ай бұрын
I like the style of this guy's talks.
@MsHofmannsJut
@MsHofmannsJut Жыл бұрын
It’s sad that many things that work great as opt-out defaults are opt-in optionals in C++. So much history.
@BGFutureBG
@BGFutureBG Жыл бұрын
exactly, leads to all this keyword mayhem on declarations
@ArtoPekkanen
@ArtoPekkanen Ай бұрын
Make all containers delete copy/copy-assign constructors, use static constructor functions that return "value or error" semantics object like std::expected with no discard. Make all copy operations explicit function calls that construct std::expected style object in place and return it to be assigned by nothrow move/move-assign. The returned "value or error" object should be switched to figure out if it has value or error, or error with what value. Boom, no need for exceptions, not even for constructors since they should be non-throwing.
@multiHappyHacker
@multiHappyHacker Жыл бұрын
I kind of want to buy one of those puzzler books now.
@artemp.2122
@artemp.2122 Жыл бұрын
58:30. May be compile time regexp check can be used to verify mode? Assuming that it remains of string-like type
@AhmedSam
@AhmedSam Жыл бұрын
The only speaker that Ii love watching his seminars eating popcorn and laughing like I'm in cinema. Learn with func :D
@rainbowkennyHSJ
@rainbowkennyHSJ Жыл бұрын
I don't understand the second code snippet of the factory method at 51:19. It doesn't take a int of widget_type as a parameter. I guess you will need to have some sort of switch-case matching against the enum inside the function to generate objects of WidgetType. What if an out-of-bound widget_type is passed?
@haxpor
@haxpor Жыл бұрын
It looks to me C++ has complexity ahead of its time. std::expected (c++23) is Result in Rust that has long been there making code totally clean and clear in handling error. Thanks for video!
@intvnut
@intvnut Жыл бұрын
Regarding the very end of the talk: fuzzing tools are definitely and important part of your arsenal. I earned my Knuth Hex Dollar with the help of a fuzzing tool.
@KX36
@KX36 Жыл бұрын
the answer to the question "what should we do with this API?" is, as all answers to similar questions are, "ship it."
@toast_on_toast1270
@toast_on_toast1270 13 күн бұрын
If you return a std::expected/std::variant for error handling, does it break return value optimisation?
@zxuiji
@zxuiji Жыл бұрын
Doesn't matter if systems use their own bitfields, you just need conversion functions to switch from their bitfield to the library bitfield, I'm doing that for my graphics library wrappers, I can then just grab the raw bitfield before entering the main loop for what I want to always use and use the raw passthrough of the same function, saves time while I always have the option of using the wrapper function for more simplicity, e.g. int clear_bits = pawvfx_get_clear_bits( PAWVFX_O_COLOR | PAWVFX_O_DEPTH ); while ( ... ) { // raw version of pawvfx_clear_bits which is just a thin wrapper around this and the above call pawgfx_clear_bits( clear_bits ); ... }
@Milan_Openfeint
@Milan_Openfeint Жыл бұрын
Shouldn't you make a "ClearBits" type instead of converting int to int? Easy to forget to convert. Wouldn't PawVfx:: be better than pawvfx_ ? People can shorten it with "using" if they want to, plus code hinting works better.
@johnmcleodvii
@johnmcleodvii Жыл бұрын
I've had a minor problem with a parameter to fseek destroy 2 hard disks. Fseek takes a parameter describing where to seek from (beginning, end, or current position) and a distance to seek specified as a signed long. Seeking outside of the bounds of the file and then reading or writing has undefined behavior. My code has 2 unsigned shorts that had to be multiplied to get the position from the start of the file to write the block of information. Unfortunately I forgot to cast the shorts before the multiplication and ended up with a 1 in the MSb of the resulting unsigned short. This was then sign extended into long that was used to seek from the start of the file. Unfortunately on that computer with that OS, and that hard disk this overwrote part of the engineering sectors of the hard disk. The manufacturer declared the HD was unfixable. The second drive was destroyed during single step debugging to find the bug and I went one step too far.
@lucasrangit
@lucasrangit Жыл бұрын
At 58:32 what's the correct option for stringly typed OS dependent parameters?
@hpesoj00
@hpesoj00 2 ай бұрын
Probably force the string to be wrapped in a strong type.
@JosefdeJoanelli
@JosefdeJoanelli Жыл бұрын
I had no idea you could delete any old member function. Without being able to use auto parameters, I wonder if you can write a deleted templated function and then write a specialisation with its explicit parameter types that you want to persist?
@gnolex86
@gnolex86 Жыл бұрын
Yes, you can define a function template as deleted and then define specializations to be used. You can even use this to split declarations and definitions to separate files and allow users of your code to define specializations for their own types. Be aware that explicit specializations of function templates have somewhat non-intuitive rules when it comes to picking which specialization is used by the code and you could very easily make your life a debugging nightmare if you're not careful.
@artemp.2122
@artemp.2122 Жыл бұрын
51:05. With WidgetType we lose an advantage of factory idiom that allows user to do not know concrete type to be created (forward declaration included). Or do i miss something?
@IsmeGenius
@IsmeGenius 7 ай бұрын
Kinda of a late answer. But yes, you lose this "advantage". But the first API had WidgetType as an enum (or int), so you conceptually did know the type, you just didn't know the C++ type. And function returned a base instance you can't do anything with and will probably cast down anyway. Sometimes it is truly beneficial to caller not to know anything about returned type, but that includes a conceptual type as well, you pass parameters to a factory function (which is often also virtual), which would make sense for any type of returned value.
@AngeloMastroberardino
@AngeloMastroberardino Жыл бұрын
(maybe naive) try-catch question. I tend to wrap most of my function's code in bug try-catch block, and usually return a value or a null/equivalent in case of inner exception. My intent is to have the client of my API not to crash on an input error, just have an error logged, and return a null value. Anyway, he'll have to check if the value is null, and the he can decide what to do with it. Is my try-catch inside all functions making my API slow? Am I abusing it ?
@dagoberttrump9290
@dagoberttrump9290 Жыл бұрын
Yes. It's double slow as you throw an exception AND the user has to branch on your null value. Just let the exception pass up the call stack to be handled by the user.
@az-kalaak6215
@az-kalaak6215 Жыл бұрын
From what I understand, you could (except if you have a C-style api implemented in c++) let the exception reach the developer part as long as it's documented, and consistant with what you already have. Since exceptions are supposed to mark an error state (it's in their name, exceptional) the slow-down part should not really be an issue (they should not happen a lot) ofc, exceptions your side (not related to the user) must not pass or pass as internal_exceptions (since the user cannot fix them) If you fear the inputs errors will be many, maybe you could expose a function throwing them all, and a function ignoring them? with defined behaviour in a non-critical piece of code, that could be a solution If i'm correct, try catch are supposed to be no-op except when having a throw
@ZarviroffSerge
@ZarviroffSerge 8 ай бұрын
The vector empty() const. A verb? On a const? ahahaha)) This is a good talk.
@jaybee9054
@jaybee9054 Жыл бұрын
Viewing Jason's CppCon talks from older to recent I wonder what's the ultimate objective for the beard..? 🤣🤣🤣
@felixbors7546
@felixbors7546 Ай бұрын
Moving up and down does little for the online people.
@stephenjames2951
@stephenjames2951 Жыл бұрын
Dynamic and interesting speaker.
@smellytaint
@smellytaint 6 ай бұрын
13:36 what would be the point of having a nodiscard enum??? Enums are just used to create symbols that hide magic numbers in the code right? So why would anyone want to have an enum that is nodiscard????
@potter2806
@potter2806 Жыл бұрын
We've made lots of tools, and the hardest thing ever is the correct usage of tools.
@meanmole3212
@meanmole3212 10 ай бұрын
And that's because the language itself is a mess that allows you to do all the stupid things you can imagine unless you sugarcoat your code with esoteric keyword noise, and it's hard to believe doing even that would guarantee reasonable protection.
@whydoineedausername1386
@whydoineedausername1386 Жыл бұрын
Isn't casting -42 to WidgetType UB? The valid range for an enum is the full range of ints using as many bits as required to represent the largest member of the enum. With two members you only need one bit => 42 is out of range. With three members you have four valid values, so there are more valid values than listed most of the time, but it's not the closest integral type
@scarletlettersproductions4393
@scarletlettersproductions4393 Жыл бұрын
That's not the case for enum classes, they can take the full range of values of the underlying integral type (which when unspecified is int). The better way to use an enum class and limit the number of valid values here would be to specify the underlying type as bool.
@marcbotnope1728
@marcbotnope1728 Жыл бұрын
Off to remove all std::optional
@sawyerwest3990
@sawyerwest3990 Жыл бұрын
@15:53 is there a way to overwrite the defaults in C++ once per file?
@BGFutureBG
@BGFutureBG Жыл бұрын
What's a "DSL" that he so often talks about? 🤔
@CHR73TANGO
@CHR73TANGO Жыл бұрын
domain specific language, en.wikipedia.org/wiki/Domain-specific_language
@zxuiji
@zxuiji Жыл бұрын
45:47, For the swapable strings thing a simple solution would be to just add a regex compiler attribute that can be used on string parameters, something like: #define PATH_ATTR __attribute__((regex("^([A-Za-z]\\:)?..."))) #define FOPEN_MODE_ATTTR __attribute((regex("^[rwx]+\\+?$"))) FILE *fopen( PATH_ATTR char *path, FOPEN_MODE_ATTR char * mode ); Sure it's not built into the C/C++ language but it works as a simple fix that can be used in both languages
@ayubyun
@ayubyun 11 ай бұрын
hello c++
@NoNameNoShame22
@NoNameNoShame22 Жыл бұрын
at this point make us all just write template metaprogramming in c++ and remove function bodies, who needs it
@PixelPulse168
@PixelPulse168 Жыл бұрын
ah, opengl has get_last_error. opengl is so old.
@MichaelPohoreski
@MichaelPohoreski Жыл бұрын
That's because OpenGL came from IrisGL. Both were written in C before C++ and exceptions even existed.
@BGFutureBG
@BGFutureBG Жыл бұрын
WINAPI as well
@fcf8269
@fcf8269 3 ай бұрын
These days you have hard time to find people that are good communicators on the subject of C++ and at the same time behaving like they are in a professional setup and not on a late night show. You can be educative and funny at the same time, without end up doing "comedy" and sarcastic comments. But once you make this a transcript and clean it up from all the nonsense, there is a lot of good material here.
@vincei4252
@vincei4252 Жыл бұрын
in main() { auto l = [] [[nodiscard]] () -> int { return 42; }; } C++ makes me want to cry trying to compete with APL by any chance ? And people back in the day used to describe plain old C as looking like modem line noise.
@ruadeil_zabelin
@ruadeil_zabelin Жыл бұрын
it's not great but you get used to it. I have no problems reading this anymore. It's the downside of an old language that needs to keep full backwards compatibility.
@paulselormey7862
@paulselormey7862 Жыл бұрын
Back to Basics?
@neuzhong
@neuzhong Жыл бұрын
C++ become more messy
@kierengracie6883
@kierengracie6883 Жыл бұрын
So many gotchas, so many guidelines... good for selling books but not for production code? This highlights everything that is wrong with C++
@kierengracie6883
@kierengracie6883 Жыл бұрын
* not good for production code because you can't hire anyone and trust them to write correct code unless they have read all the books or already an expert... how is that supposed to work?
@kierengracie6883
@kierengracie6883 Жыл бұрын
In other words: C++ - it's like trying to make an octopus by nailing 4 legs onto a dog
@fareloz
@fareloz Жыл бұрын
First 5 mins I was thinking this guy needs less alpha-male energy on stage and more informatin to present. But in the end it wasn't that bad, I noted down few intresting things. 3 out of 5 talk.
@vipham9355
@vipham9355 18 күн бұрын
why he can't stand at one spot, just feel dizzy by watching his talk!
@rationalcoder
@rationalcoder Жыл бұрын
This contains a few nuggets of good advice, but focuses too much on trivialities and a few falsehoods, like using exceptions in the first place. Yes the defaults of C++ are bad, and the language itself has many issues, but often hacking in a solution in C++ is just not worth the effort vs time spent debugging/testing. Simpler APIs with fewer concepts, but with well known usability issues, are often better than complicated APIs with extra concepts for safety. This is because the ways in which simple APIs can be used correctly and incorrectly are well known, whereas the ways in which an API with bespoke concepts can be used correctly and incorrectly is unique to every library that decides to go this route. Sometimes the answer is just that C++ sucks. Fix the language, put up with it, or work with a different one. It's for this reason that many people including myself simply don't use third party C++ libraries if we can avoid it. I always look for the C version of a library.
@curtmcd
@curtmcd Жыл бұрын
I don't know. I've been writing fopen() calls since the 1980s and have never come close to ignoring the result, swapping the arguments, passing in NULL, or accidentally ignoring errors. That FilePtr abomination seems much more prone to issues. It obscures what's going on with long messy code incorporating a dozen implicit complex machinations with way more obscure failure modes.
@RigelOrionBeta
@RigelOrionBeta Жыл бұрын
I think the point is for things you don't understand. Of course if you know an API, you'll know what not to do. But if you want to use an API and don't necessarily know every little thing about it, then doing things this way will essentially force you to.
@GaryHutchins
@GaryHutchins Жыл бұрын
Remembering that you are not every programmer is always a good idea :)
@cdamerius2895
@cdamerius2895 7 ай бұрын
I agree. To me the FilePtr way of implementing seems to go into the wrong direction. I think it is even worse than the original fopen. The only reasonable thing in the FilePtr implementation to me seems to be the [[nodiscard]], although i never ignored fopen's result, so even that does not seem particularly beneficial. First, I don't like that the FilePtr implementation is taking an std::filesystem::path&. Everyone uses a different way of handling strings, maybe zero-terminated, ptr+size or beginptr+endptr. Maybe you get your filepath out of some library. Now everytime you want to call fopen, you have to artificially create an std::filesystem::path object just to call fopen (I think this introduces a copy, doesn't it?). An implementation should settle for the lowest common denominator, which is probably ptr+size or beginptr+endptr. This way, whatever the format of your path string is, you have a way to call this function without creating overhead. Second, I don't like the way the fclose() call is obfuscated. This was obviously because we want RAII, to cleanup the resource automatically for us. Okay, we often forget to do that, are we? I am not particularly a fan of this. But then again, there are cases where you want the file object to outlive the current scope. With the FilePtr implementation, you have to know that it automatically fcloses, and need a way (probably a move or something) to transfer the file object to another scope. That seems convoluted. In the original fopen() implementation, that is not necessary. However, i also have to concede that many people are probably used to this kind of way of doing things. Third, I would really like the mode string to be something like a bitwise OR of mode flags. Jason mentioned that these are somewhat complicated. But i mean, we have r, w, a, b, +, maybe a few more if I am not mistaken? That does not seem impossible to do with a bitfield. I would generally go for this kind of approach. Fourth, i would probably not want the function to return a FILE*, but instead let the user provide the location of the FILE, and fopen() fills that in. Now whether you can do that probably depends on the way that the file control actually deals with FILE's, so that may not be possible (i am unfamiliar with the file controls internals). But at least, this way the allocation would lie on the caller's side. For example, I could now store the FILE object on the stack, which i can't do with the current fopen() implementation. Regarding error handling, it really depends on how complex the error handling needs to be. But here I would probably either return an error code, or provide another parameter where fopen can place the error details into. I generally agree that having something like get_last_error() or errno is a bad idea.
@Knirin
@Knirin 3 ай бұрын
@@cdamerius2895Yeah, there are a lot cases where detecting that it failed is the most important thing because you can’t fix the failure in your code. Giving the user/log a good error so they can fix the problem is then second.
@kaiszuttor
@kaiszuttor Жыл бұрын
Quite a dry humor 😅
@zxuiji
@zxuiji Жыл бұрын
23:30, I don't c why don't just make an accompanying function called kill_widget or something, then even if the return type is unclear for make_widget (because like f**k am I adding unnecessary garbage to a simple "widget* make_widget( ... );" statement - by the way, bad way to make any widget, always dedicated creation functions like make_txtbox or make_dlgbox) it is always clear how to cleanup the pointer later 26:37, again a kill_widget() function would clear up the headaches real fast
@XeroKimorimon
@XeroKimorimon Жыл бұрын
Same reason why C++ avoids directly calling new + delete. It's faulty, and from skimming the code, it's hard to tell who _should_ be responsible for deleting a pointer. Instead of kill_widget, std::unique_ptr directly says "I am the owner of this pointer, I have responsibility to delete this widget" as std::unique_ptr will delete the pointer when it falls out of scope
@zxuiji
@zxuiji Жыл бұрын
@@XeroKimorimon how is kill_widget obtuse??? you pass it the pointer and clear your own, whether it was static, dynamic or whatever it's the library's job to distinguish that on it's OWN pointers, the library is ALWAYS responsible for the details of a pointer it hands out, it only needs to be told when to cleanup. There is nothing to confuse, additionally my method is C compatible whereas anything involving new,delete,unique_pointer etc is C++ only, a library should target maximum exposure and starting with C is the sensible choice there, not C++, sure it can use whatever it wants under the hood and provide extensions to it's core API according to language but the goal was to achieve max exposure while being simple to use, telling the developer "just pass it to this/these function/s to cleanup" is ALL it should do at the core API
@sledgex9
@sledgex9 Жыл бұрын
@@zxuiji Your kill_widget is basically the equivalent of delete. Like all the C libs (eg gtk) having a my_object_free() function for **each** object. Then the caller needs to manually track all exit/return points to clean up (aka call kill_widget). It breaks RAII. The only way to make it work is the caller to wrap the returned raw pointer into a unique_ptr with a custom deleter which uses kill_widget. You've basically thrown away all modern C++ and created a horrible API to use, which is also easy to use wrong ( = the caller forgets to call kill_switch at all return points). I think you totally missed the point of the presentation.
@TheJoKeR7
@TheJoKeR7 Жыл бұрын
@@zxuiji so you are basically building a C library/API and not a C++
@XeroKimorimon
@XeroKimorimon Жыл бұрын
@@zxuiji I never said it was obtuse, I said it was faulty. Like if you had.... Widget* a = make_widget(); And then somewhere else in your code you got Widget* b = a; Who should call kill_widget()? a or b? Replace the return value with a std::unique_ptr and redo the example std::unique_ptr a = make_widget(); std::unique_ptr b = a; //oh no compiler error, you can't copy unique ptrs In order to make the example above work, b would have to be a normal Widget*, and in C++ land, that means b shouldn't call kill_widget(), and a will automatically do that for us, so we'll never forget to call kill_widget(). It also helps us understand that a's lifetime should be longer than b's lifetime, anytime this is false, we clearly have a bug and either make sure both a and b have equal responsibility for it's lifetime via std::shared_ptr, or change something in code to guarantee that a outlives b
Delivering Safe C++ - Bjarne Stroustrup - CppCon 2023
1:29:16
Don’t take steroids ! 🙏🙏
00:16
Tibo InShape
Рет қаралды 28 МЛН
маленький брат прыгает в бассейн
00:15
GL Show Russian
Рет қаралды 3,9 МЛН
YouTube's Biggest Mistake..
00:34
Stokes Twins
Рет қаралды 66 МЛН
What is a REST API?
9:12
IBM Technology
Рет қаралды 1,4 МЛН
Let's get comfortable with SFINAE (C++)
35:55
platis.solutions
Рет қаралды 2,7 М.
C++ Weekly - Ep 427 - Simple Generators Without Coroutines
5:27
C++ Weekly With Jason Turner
Рет қаралды 9 М.
-memory-safe C++ - Jim Radigan - CppCon 2022
1:05:45
CppCon
Рет қаралды 21 М.
C++ Weekly - Ep 426 - Lambdas As State Machines
6:53
C++ Weekly With Jason Turner
Рет қаралды 12 М.
#Shorts Good idea for testing to show.
0:17
RAIN Gadgets
Рет қаралды 3,5 МЛН
Эффект Карбонаро и бумажный телефон
1:01
История одного вокалиста
Рет қаралды 1,3 МЛН
How Neuralink Works 🧠
0:28
Zack D. Films
Рет қаралды 27 МЛН
Fiber kablo
0:15
Elektrik-Elektronik
Рет қаралды 4,6 МЛН
Я Создал Новый Айфон!
0:59
FLV
Рет қаралды 3 МЛН