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

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Back to Basics API design I'm Jason Turner if you're in the wrong room go ahead and leave now oh there's someone leaving okay uh let's see um so my name is Jason Turner I'm host of C plus weekly and I'm an author I've got puzzle books and best practices books and if you say something interesting or have good feedback during the talk you get one of those just so you know puzzle books are meant to be fun they're not mean C plus plus puzzle books and um on Microsoft MVP since 2015. I'm proud of I am independent available for training code reviews Contracting that kind of thing if you want to get in touch with me I've got cards whatever talk to me after the session or look up that URL okay so one of the main rules in my talks are move to the front because you're going to want to interact I see you in the back section yeah um please interrupt me and ask questions this is not a hold your questions until the end of the talk talk this is a yell things out which I know makes for a terrible experience for the online people I'm sorry but uh that's how we roll here I will do my best to repeat the questions and comments as they come up this is approximately how my training days look by the way oops uh so in the topic of well speaking I got the chance to practice this talk at my local Meetup we already have a online question oh that's interesting can online attendees get books if we're interested in them you can online attendees can buy books if they are interested in them yes I will just quickly say those are the URLs for the online attendees okay I figure you had the chance to screenshot it by now if you wanted to all right um so I got this I gave this talk at my local Meetup I previewed it there which means I have absolutely no idea how long this talk is going to take because my Meetup hardly let me get past the first half of the talk within an hour we'll see what happens but I want to ask do you live in a city with a c plus Meetup go ahead raise your hand if you do sort of it's a fair number of people okay do you attend your local Meetup not as many people as who know that they have a meet-up do you live in the Denver Boulder Longmont area by any chance yeah okay okay I recognize many of you do you come to my Meetup that is in this area who's this group right here who all raised your hands you live here and you don't come to my Meetup come and I meet up check it out okay um so I was asked to give a talk about and the base Back to Basics track on API design I can do that said me sorry that's uh no I'm just kidding okay so more seriously this is outside of the kind of things that I tend to talk about at conferences and this is largely taken from material I normally leave for my classes so you should hire me to come and do training at your company shamelessly self-promoting here but I will be focusing on number 32 for my best practices book here which is make your API hard to use wrong that's what we're going to be focusing on during this talk I am going to show you code and you're going to tell me if the API is easy or hard to use wrong or if you want to argue some gray area so highly interactive is the goal and maybe something about const expert I have to stay on brand right for those of you who are laughing okay so let's take the humble standard Vector C plus 98 declaration and I ask you is this slice of the API easy or hard to use wrong well okay okay so who says it's easy to use wrong okay who says it's hard to use wrong oh okay so for those of you who said easy to use wrong why it looks like a verb is it a question or is it a command okay um so what does empty do it returns true if there are no elements in the vector what happens if I drop the return value from this function is it a bug if I drop the return value from this function and my code in the calling code does my code have a bug probably who says yes definitely a bug if I call this function and I don't do anything at the return value okay who says probably a bug who says nah that's fine hmm okay and then I asked the question of what kind of error handling does this have but we'll get back to that in just a second um so how would you rewrite this no discard thank you Anthony or is empty so something like this perhaps no discard is empty okay so we'll dig into what no discard means in just a second here are we at the easy or hard to use wrong now do we say now that this is okay uh let's start with the easy side who says this is easy to use wrong no one is raising your hands now interesting okay who says this is hard to use wrong who is trying to catch up and is still not sure what we're talking about okay uh we will we'll dig in a little bit more but I also ask is there any uh error handling to this function is there any any reasonable way that an air condition could occur here oh someone's wait who said yes was it Shea no sorry uh no okay okay what kind of who said yes for real where was that okay why did you say yes the vector might be moved from if the vectors move from the standard actually does specifically say well it gives us guidance here right so something that's been moved from should be in what kind of state valid but unspecified so it's possible I've moved from this and I call is empty and it returns false or it's possible it's been moved from and I call is empty and it returns true but in either case it's not an error Condition it's an it's an unspecified condition I would argue what's that the signature allows it to throw and you don't know if it throws or not is that better okay so I specified no except here do we have anything left to talk about for this function I'm actually asking this it's not a trick question here okay so we'll start with uh use better naming so naming is hard as we know the two hardest problems in computer science are cash invalidation naming off by one errors scope creep and Bounds checking I I shamelessly stole this just for the record apparently the first two is from Phil Carlton it's unknown who added the off by one and Dave stagner added the the rest of them uh okay so let's start with notice guard this instructs the compiler to generate a warning if a return value is dropped it can be applied to types or function declarations now I know for those of you who are like standards committee members you all argue that the compiler is not required to give you a warning if you ignore this return value on line four your compiler is not required to give you a warning it's a quality of implementation issue if your compiler doesn't give you a warning in this case you should probably find a new compiler this is pretty clear-cut who's familiar with no discard it's a pretty good group in the room okay did you know that c plus 23 fixed a minor loophole in the standard that did not allow you to apply notice guard to a Lambda you have to appreciate the sheer quantity of brackets in this case foreign so we are specifically applying no discard to the call Operator of the Lambda in this case notice guard can also be applied to types so in this code I can say I have declared some type it is an error handling type if you ignore anything that returns this type then I should get a warning from the compiler familiar with this no so if you are like from bjarnes opening keynote when he was talking about in the case of having error code types perhaps if you need to avoid exceptions in that code then uh this is this is very good thing to do make a strong type that is no discard there's one more place where no discard can be used does anyone know where it is Constructors so you can Mark a Constructor as no discard um honestly I have kind of a hard time coming up with very good examples for this particular use case you could say unique pointers non-empty Constructor perhaps Ned's notice guard but whatever case you might have if you pass in some value that you need raii who is the person who's talking about Rai as I walked into the room earlier it was you right yeah um if you have some sort of owning type or something like that then you can mark the one Constructor that needs to you know say it's an error if I call this Constructor and then immediately throw away the object but not an error to throw away you know one that was default constructed okay so no discard can be used to indicate when it is an error to ignore a return value from a function can be applied to Constructors as of C plus plus 20 lambdas in 23 and can have a message to explain the error no discard some reason and it should be used extensively any non-mutating getter accessor const function should be notified now from the hands that I saw raised in the room I gather that you all have a pretty good grasp on no discard uh should let's just say for argument's sake should the cosine function be no discard yes you all agree okay um should Vector insert be no discard or ask a different question what in the world does Vector insert return an iterator to the element that was inserted or the first cell wait what oh if you insert a range then it returns an iterator to the first element in the range that was inserted okay okay so should insert be no discard no there's plenty of logical use cases to say I inserted something and I don't need the iterator to the thing that was returned question yes can no discard be used for oh like could I mark an enum type like a you know I'm struck or whatever as no discard I don't think you can does anyone know no no you can't or no you don't know I don't think you can who asked that tell them to write a paper and submit it to the committee a night I believe that sounds that sounds like a really good proposal doesn't it it really honestly does I wish I could give you a book uh tell them to DM me and I will send an ebook code to lean Pub to get an ebook all right um so no discard is checked by our compiler for us yes uh so the comment was that I'm arguing that we should put notice guard everywhere where we have const and that will clutter our code like no tomorrow is that what you said okay um yes Vittorio says go to his talk on Thursday he talks specifically about it is that right I got that right okay um yeah yeah it's kind of it is a thing um and we reach a certain point see I gave a talk called applied cons no applied best practices at cbpcon 2018. I guess 2018. um and yeah you do reach a point where you end up with like the no discard constex where blah blah blah trailing return type no except and your function declaration is this long um we can certainly argue that at the well if CBS plus were designed today we would have taken a different take to our defaults that's that's the the truth but that's where we are at the moment okay so no accept no except notifies the user and the compiler that a function may not throw an exception if an exception is thrown from that function terminate must be called so I have this code the compiler is required to accept this code just for the record where I declare it node accept and then I throw an exception in the body of it uh generally your compiler is going to give you a warning here and tell you that this function is always going to terminate I don't think all carbon pilers can give you this warning in all cases but actually I'm kind of curious let's just see real quick hey look warning so that's GCC throw will always call terminate and claying gives a similar warning okay so the try a catch on in Maine on line seven and nine is irrelevant it's removed by the compiler well depends on your compiler GCC probably removes the try catch and it relies on its run time to catch an unhandled exception and thereby implicitly call terminate claimed probably actually inserts a terminate call and leaves the try catch there something along those lines GCC is scarily good at optimizing the stuff around no accept and exceptions so summaries thus far use better naming use no discard with reasons liberally use no except to indicate what kind of error handling is being used in your function foreign talks this week on Factory functions factories I thought I saw one on the schedule but then are you giving a talk on factories okay when is your talk on factories Thursday are you up against Vittorio after Vittorio okay Okay so Thursday which room is it in this room what time three and four Thirty four forty five okay um in this room we've got notice Garden Factory functions discussed okay so I'm not hopefully going to overlap too much with what you're talking about but who was in the last session that was in this room small handful of people uh so is this Factory function easy or hard to use wrong foreign easy to use wrong okay um what happens if I ignore the return value leak but might leak okay it could be another reference to a thing it could be a thing that's in some sort of static holder of all the widgets that have been created so far it could be a look up into a Singleton it could be something newly created on the Heap it could be a q object that's going to automatically clean itself up regardless because of its internal reference counting thing right problem is this API doesn't communicate anything um what is the possible range of input values what did you say yeah zero through five okay so how would we rewrite this one is it any got any ideas make the antoninum return of value what type of value a widget theoretically okay if you look at this do you assume polymorphism is coming into play that there is an object hierarchy here type erase pointer I I assume if I see this if I see a factory function I assume there's probably some sort of polymorphism coming into play that doesn't necessarily mean that we can't return a value though because we could have a type erased wrapper something like stood function or something like that like in concept and principle around it yes could use the unique pointer yeah that's where the next slide is going anyhow no discard of a unique pointer so uh this is slightly better at least um who would say that this is easy to use wrong still Fair number of people okay I have books I haven't given away yet so that says I still have 38 minutes is that right that's right okay good um okay so I've now got an enum class and I'm passing it to make widget is this easy to use wrong or hard to use wrong harder says Marshall why harder yeah but well okay what is the what is the range of input values to this function right now slider and button you say are the range of a valid input values okay what is the valid range of the type called widget type what was that all energy values who said that well you're supposed to sit closer if you want a book do you want a puzzle book or best practices book best practices all right thank you there you go you're welcome um yeah uh as though the underlying type of the CNM classes end um we have to try harder to use it wrong one of my Meetup members described this as being uh in a pit of success and he said I got that from some other author I don't I don't know the Genesis of that phrase um but you know you you have to try you have to work at it well it feels like you have to work at it but yeah wait sorry to do that okay and that will work okay so we were just told that I don't have to use a static cast I can just do widget curly braces stuff in whatever minus 42 as of C plus plus 17. and the compiler will accept that sounds like a failure why was that added in 17 what did I miss uh I have to be careful to not get feedback from the speaker okay so you can create bytes out of constants well okay um question that makes sense actually kind of yeah uh we have an online question yeah if the factory function is an API exposed by a dll exactly you're still be used yeah okay so if it's a function exposed by dll yes you can still use Smart pointers you do have to be well I promised that I would not mention ABI in this talk um but you have to be aware of your ABI boundary in that kind of situation but yes you definitely can and in fact where I have most done code like this is actually a loadable modules that you load that introduce new types that might be created from a factory function and that I should probably look at the camera if since I'm speaking to the person who asked the question um uh and then that definitely raises all kinds of questions about what is the input parameter set here and where do the types come from and who knows because in a dll you can absolutely extend a class that was defined in a different dll and do things okay so this is better uh what about error handling we we could talk about this for probably the remainder of the 34 minutes that we have uh what happens if I return a null unique pointer is that an error do I throw an exception um how do I report what kind of error happened I don't know I a very few things about this make me particularly happy uh because I feel like you always have to check the thing returned from make widget to see if it's null because we don't have any guarantee that I was not returned an all unique pointer here even if I were to mark this like you know no except or whatever right uh is it possible to fail to create a widget probably we don't know we don't have any way of communicating that um should it throw an exception or not exactly all the questions that I just mentioned and exactly I got ahead of myself we'll come back around to this in a little bit though okay so never return a raw pointer raises too many questions who owns it who deletes it is it a Singleton Global I don't know don't return raw pointers consider some sort of rapper something that communicates this we can make a zero cost wrapper around our pointer type and say I'm handing you an owning pointer or non-owning pointer or a whatever something that you're supposed to clean up something that I'm expected to clean up we can communicate that with types we can use not null pointer if we want to from the guideline support Library we can communicate this and have a consistent error handling policy oh my goodness please have a consistent error handling policy in your library we want one consistent method of reporting errors in your library or making it very clear to the user like there's a few things in the standard Library that have a throwing version and a non-throwing version right Dynamic cast it's been around forever if you pass a pointer into Dynamic cast then you get a pointer back and it's up to you to check it to see if it's no point or not if you put a pass a reference to Dynamic cast and you can't do the dynamic cast then you get an exception throne but this is the API theoretically makes it clear as to what's Happening Here strongly avoid out-of-band error reporting do not make the user of your library call get last error to find out what error happened last why not because nobody does that thank you Marshall but I'm going for a slightly more specific reason than that too threads you just destroyed the ability to make your library multi-thread friendly um we do we don't like I mean you air no that is actually a thread local as of C plus plus 11 technically it doesn't have the threading problem that doesn't make it a good solution though so we want to make errors impossible to ignore I I say don't return an error code I would prefer exceptions because they are quite impossible to ignore unless you feel like wrapping everything in try catch dot dot and then carrying on with your life but the real Point here is that uh that I don't have actually a bullet point for is you don't want it to be possible for your application to get into an unknown state if there's an error and it's possible for the user of your error or a user of your library to ignore that error now your library is in an unknown State that's back and please don't use optional to indicate an error condition it does not convey a reason and the reason becomes out of out of bound out of band yeah I meant to put out a band there so it returning an optional from your library just makes your user go and try to call this get last error or they don't check whether or not the optional is set or every single function call into your library gets wrapped into uh you know if the optional is set then continue with normal code flow or something like that does anyone have strong opinions about this does anyone disagreeing with me at this point exceptions are slow only if you actually throw a lot of them exceptions aren't real time safe that is true that's a different condition for your uh Library still don't return optional in that case return something like expected something that forces the user to not ignore the re the error condition have an expected type that is no discard and you know make the user say did an error occur here and deal with it interesting that's when the cameras came out expected as should be context for friendly right please tell me it is it is okay good thank you yes what about preconditioning how to report broken preconditions how to report broken preconditions um well I'll give an opinion on that uh well that's a good question we didn't get contracts right um you you're still going to have to deal with it some way right you can't return you know you invoking well let's just say over the next rest of the slides we will um we will kind of talk in a circle around that issue and hopefully make it harder to pass things that have broken preconditions one more question yes if stood expected is C plus plus 23 what do you recommend today um it is well first of all you can download an implementation that'll work with your compiler today that's not difficult uh you could do something like a variant of error or return value that's effectively what stood affected is um but or create your own version that's similar it's not it's not an overly complicated concept I don't think although I might have standard Library Implement implementers in the library in the room who would disagree with me on this no okay now I'm not getting disagreements from them all right summary so far better naming no discard all right picking up from there never return a raw pointer provide consistent impossible to ignore error handling within band reporting of what went wrong F open is this easy or hard do you oh is that a question should I get a book is it going to be a good question stood error code uh yeah I don't I don't have anything to talk about stood error code in here I am I've never used it in real code I've watched several presentations on it and felt like I never really fully understood how I was supposed to use it in real code I have to be honest there I don't know is there someone who wants to make the defense for stood error code you can have the mic for a second is anyone going to seriously you want to make a defense for it well then come up here to the mic make a defense for it are you willing to do that we can turn on that mic right cool that mic will be turned on in a second I I I'm not going to argue with you at all because I don't feel like I know enough to argue with you well it solves the problem of having different error code domains in a way that works pretty well effectively I agree that trying to implement your own uh genre of error code is a little weird boilerplate that you have to do but other than that actually using in practice is is fairly easy does that it has good ergonomics once you've actually gone through the boilerplate work yeah I mean like if you're using azio you're using it a lot okay and so or at least something like that I've used the Boost version a lot but uh you get you get System error codes I have my own error codes you can extend it it works well and it's easy enough at like run time or compile time to do a switch and say what if it was this error code then do this kind of scenario handle or kind of thing I don't know about context very but it definitely runtime oh that's right I don't think it is const expert capable is it I don't remember now yeah you can you can look for your exact error code type you can print it out nicely you can print out the category which goes to I mean it's a little awkward it has to go back to some static name for the category type but oh right mostly aside from those it's a little weird you get used to it and then it's easy okay and it works thank you very much do you want a one of these books sure go ahead and pick if you want a puzzle book or a best practices book just go ahead and grab one we got a question yes oh a question in the audience you're pointing yes go for it wait wait wait wait wait wait wait sorry yeah so for the factory function that's not supposed to return at all can you use the factory function or excuse me the GSL not in all wrapper yes absolutely definitely and a question from online okay several several does this mean you think it's the library author's responsibility to enforce exceptions happen if they do in client applications I'm not sure I fully understand the question is it the library author's responsibility to make sure exceptions occur in client applications to make sure exceptions occur in client applications um I I think the way that I'm going to phrase my response is similar to what I've already said or as the the subtitle is it's the library author's responsibility to make your library hard to use wrong and that takes creativity sometimes because you have to decide is that an exception is it an expected is it a stood error code is it whatever uh that's I I don't know if I'm I feel like I'm just repeating myself I'm not sure if that's a appropriate answer but what's the what's the next uh uh what about a structured binding with no discard a structured binding with no disk guard oh uh yes so with with most compilers today and I know I have an idea where that question is coming from because when compilers first supported structured bindings they were not very good at this kind of warning reporting um with compilers right now if you de-structure the return value from a notice guard function as long as you use at least one of those elements you don't get a warning it used to be that you had to use all of them otherwise the compiler cried to you and last question what about stood optional what do you mean by out of bond out of out of band uh so it's the by out-of-band error reporting I mean if someone um if you get back an optional then you're forcing the user to do a separate function call to find out why you got an empty optional so that's that that separate action is what I mean by out of band I want when you call the function to know now what kind of error occurred because again threading issues uh and just in general because the user is going to ignore the error code if you make them call a different function okay uh F open easy or hard to use wrong easy it is wrong F open the humble The Humble F open and I know yes my Meetup members gave me a hard time for this because yes this is an old school posix function and this is how apis were defined back then that doesn't mean we can't learn something from it today um how is error handling done in this function air no I think it's there no null pointers so he said wait what's that right okay what happens if I drop the return value leave an error an open resource yeah what is the format for mode it's practically a DSL really we'll get into that a little bit more what happens if I called this that's correct right what happens if I call this crash yeah uh so this by the way is a violation of a precondition which is why I said we'll kind of dance around this topic more um how would you rewrite this oh yeah go ahead you raised your hand yeah uh did you say path wait Cafe a path named object okay right right um and what else enumclass for the mode I don't know if we can get away with that because mode is big and weird but what's that uh oh no no no I'm sorry now this is falling down I've got three different people talking you started talking first make file a value type instead of a pointer so that if you drop the object you don't lose the resource anymore that's of that is vile option there's someone over here right it's binary make an enumeration I will get back to that in a second okay uh this is a solution by the way for those of you who are not used to the versatility of unique pointer you can pass your own deleter for any kind of pointer thing so we can start to just make a wrapper if you will around here that's a little bit more modern and still work with these operating system apis that exists um so we all I think still agree this is hard to use wrong we've already commented on this so we would do something like this perhaps I think this answers several of the comments okay oh can I make a using declaration no discard that's the ultimate question and uh no not in any way that I know of I don't think the grammar would allow that okay so this is easily swappable parameters these character pointers I'm going to start moving slightly faster to see how far we get into this talk two or more parameters beside each other the same type are easy to swap playing tight he has a check for this bug prone easily swappable parameters that solves all of our problems right as yeah it solves some problems Okay so it solves no problems so far I I at compile time I have no way of knowing if that's a path or a random string what is the fundamental problem why does this compile explicit and implicit conversions um I will attempt to hear what you are going to say yes both of them are strings who actually first said implicit conversions over here do you want a book you're getting a puzzle book it's my object lifetime Puzzler book sorry Shameless self-promotion again okay um it's the fact that these exist right there are implicit conversions this is greatly simplified just for the record because both of these are templated types but path file system path and string view have implicit conversions from character pointers this compiles it's implicit conversions so next item avoid implicit conversions Slash use strong types file system and path appear to be strong types but they're not because we have implicit conversions in a tangled web between const character pointer string string views and path types I think that this is a mistake um conversion operators in single parameter Constructors including very attic uh Constructors and Constructors with default parameters definitely should all be marked explicit so now yeah you might have for your construct X your Constructor constex Burn Notice guard explicit something something no except it's entirely possible yes sorry whoever said that earlier question yeah it would be great if the standard Library issued a warning if the move Constructor will not be used just because no except was forgotten is there an easy way to ensure such guarantee When developing a library um you're right now your compiler if you're running good static analysis on your library your compiler should catch in the case that you have and implicitly or explicitly deleted move Constructor well if you haven't explicitly discretely deleted move Constructor you won't get a warning an implicitly deleted move Constructor should give you a warning with modern tools so I was a missing no except Oh missing no except specifically sorry does anyone have an answer to that I don't know okay okay so the comment was clang actually does give you currently a warning on this and it's a really annoying and templated code because you sometimes end up wrapping types that are not movable and you have to revert to a copy but can you not use a conditional no except oh yeah that sounds bad uh so the con the comment was it does still issue a warning even if you have a conditional no except and the condition happens to resolve to false yeah well that's um okay uh that sounds like that could be better handled by your analysis tools I haven't run across that myself okay so just I want to throw this out here has anyone seen this kind of construct yeah I've seen this yeah um I'm guessing most of you probably haven't written code like this this uh Auto parameters that's C plus plus 20 you could make it a template function just as easily this is implicitly a template see what's 20 implicit template here and I'm basically creating a catch-all I'm saying if you didn't pass me exactly a file system path and exactly a string view then I'm going to fail to compile because it's going to catch this explicitly deleted catch-all you get a compile time error thank you I don't think so yeah he said if I have to pass an explicit string view then I've lost the benefits of string View and I said I don't think so uh no it's not a problem that the return type is missing because this function is never going to be invoked it's the compiler is going to catch it anyhow because I deleted it so this is actually a reasonable way to do this it looks weird but it's a reasonable way to do it um yeah okay so you could you could play with this if you wanted to I don't have a problem with this because it's not very hard to call string view braces and explicitly create a string view to me the strength of the string view is not the fact that they can be implicitly created the strength of a string view is that it can take any string like thing and I don't have to have a template to take any string like thing so delete problematic overloads any function can be deleted if you delete a template it becomes the default match for any non-exact parameters and that prevents implicit conversions so the new items use stronger types and avoid default conversions and sparingly delete problematic overloads I'm guessing if you go back to your job on Monday and start typing a bunch of equals deletes to avoid implicit conversions everywhere your co-workers aren't going to be very happy with you but this is a tool to make our libraries hard use wrong foreign Oh I thought you were raising your hand not for him okay yeah how should our new F open function behave if it fails to open the file I I well I would have it throw an exception personally but it could be an expected whatever all the same rules are going to apply just make it so it's impossible to ignore error that's all I ask of you if the thing that I'm trying to delete is in a namespace that I don't own is it a good idea to jump in their namespace no uh so you're effectively asking might I do something like uh namespace STD open curly do let delete of a thing you're going to be in the world of undefined Behavior if you do that technically speaking I would not recommend it no you could you could do something I think like uh you define it in your own namespace and then pull it in with a using declaration well but then STD would have to be in using declaration two I wouldn't do that um yeah I don't know I think I would be reluctant to muck around in someone else's library to do this unless it was a very big problem in your code base all right so let's back up a little bit to this yeah oh oh yes okay so Marshall said since equals delete finds things that compile time you could do it in your debug Builds on your CI and not in your release build so you don't have to worry about the potential UB or whatever might happen in the release builds but still catch up I could see you could even add a third target let's just catch my screwed up conversions build Target and never run it it's interesting idea okay um we uh sorry yes this is a slide that I haven't presented yet I've got eight minutes and 40 seconds remaining um we could do something make this templated by the way this is the slide that my or before this that my Meetup wouldn't let me get past was yeah we could do a templated strongly typed object thing and just a shoe the whole question of whether or not it's an all pointer that's been returned I agree if you're in an environment where this is possible then do this and you can even put a requirement on it and say require the thing passed to me as a base of or excuse me is derived from my widget type and then let the factory do what it knows how to do possibly um our path name and mode optional in this case uh yeah oh wait a question about which I'm getting away from the speaker uh so the question was how do I fix the problem of an incorrect widget type being passed here in this case you're just going to catch that at compile time if someone were to pass in something that your factory doesn't know how to create boom it can't compile so this is ultimately the best strategy if all of the possible things that you might throw into here are compile time known and then you can if you need to like load from a configuration file all the types of widgets that I need to create then you can do a translation from the loaded value to the compile time value uh if yeah the question was what if I static cast 42 into here I can't this has to be a type known at compile time I can't static cast 42 to a type this is a template function that takes a widget type and it's a widget type that must be derived from widget compiler this is by far the safest that I know of compile time checked way to do something like this like I think we could play this game all day and you're not going to find a way that's because it's it's it's really difficult to fool the compiler here okay um so our path name and mode optional they're not we already mentioned if you pass an all pointer to either one of them it's undefined Behavior program crash so these are I'm just gonna say for you easy to use wrong which leads to only past raw pointers for single optional objects so I ask is Str optional in this function no why would I be doing an assert on it if it's optional no it's not optional um is Str optional in this function yes it is I have two different code paths I check to see if it's there I do something otherwise I do something else this is the only situation where I would say you should use a string to pass to a function I have actually a stood string const pointer pointer did I do that on purpose I don't think so maybe I did I don't think so okay um but in this version like this is completely unsafe right if you're past a pointer we're talking about making your library hard to use wrong right if you're past a pointer inside your library you have to check it to see if it is a valid pointer because you're protecting your user like this precondition question I would say we're talking about making our API How Are You Is Wrong we're you know we're trying to check our preconditions or not do this what is the alternative here by the way pass a reference in that case yeah prefer reference parameters for not small not trivial objects um that's that's a much bigger conversation than we have available in the next four minutes and 18 seconds so if you're passing an INT by const reference you've probably done something wrong okay if you're passing a stringed by const reference then we're probably okay so don't pass uh smart pointers unless you need to participate in lifetime I'm gonna have to just move past that one okay so where we are at is avoid passing parameter uh passing pointers uh in general but only pass them for single optional objects and avoid passing smart pointers just as a general rule there is one problem left here which we have danced around a bit uh what is the possible set of inputs to mode I said it's like a DSL it really is uh so this is from the Linux man page for f open when passing for individual flag characters and mode Let's see the g-libs the implementation of f open and F reopen limits the number of characters examined in mode to seven uh and g-lib C prior to versions 2.14 it was 6 which is not enough to include possible specifications such as RB plus c m x e it is effectively a DSL right um current implementation of uh FD open parses at most five characters in mode so it's all over the map here and like I started this by saying we can always count on posix apis for interesting discussion seriously I mean again I don't blame them it was how things were done then but I can from the standpoint of someone who teaches C plus plus best practices I can literally pick any posix function and make like a day class about it okay so we're back to this I think we agree that there's still ways to make this easy to use wrong I don't have the delete in here but if I did um we're gonna have to discuss the question of is it possible to make some sort of compile time type checked set of things that we pass for our mode instead of just a string a ctre library for this yeah yeah Hannah's compile time regular expressions like the concepts that she does where she actually parses things at compile time I would say yes probably but just like even regular Expressions sometimes you need to make a runtime decision about what flags or what the regular expression is um let's just say I'm very much running out of time I want to finish my point here maybe this is truly an open-ended and Os dependent stringly typed thing let's just say for argument's sake that this is the API we can't do better what should I do to this API I'm looking to you Marshall you would add overloads no that's not the answer I wanted at the moment what's that with it bit Fields yeah uh yes midfields are possible but then it becomes complicated if each operating system has its own possible set of bits that could be set sorry I have to move forward to this slide because I'm out of time fuzz your interfaces do you agree Marshall okay thank you um so a fuzzer is a tool that tests your API against a set of pseudo-random inputs it generates inputs based on uh feedback from the runtime basically you need to run them with address and undefined sanitizers enabled to me this is the the next most important state thing that we need to make sure we address here fuzz your API because this finds all of the creative ways of using it wrong that you never considered um it can be used with literally any API creatively if you dig into fuzzers you're going to think it only works the things that take string inputs no no no just get more creative talk to Marshall you'll be fine um and I'm past time but use better naming notice guard no accept consistent impossible to ignore in-band error handling strong types avoid passing pointers limit your API as much as possible and fuzz your API and make your API hard to use wrong oh and enable it for context spur unless you really have a good reason not to okay so that's me I'm officially out of time you can come up here and chat if you would like to but otherwise thank you very much for attending my session
Info
Channel: CppCon
Views: 62,215
Rating: undefined out of 5
Keywords: interface design c++, memory safety cpp, c++ type-safety, c++ back to basics, C++ API, back to basics c++, c++ API design, cpp back to basics, Jason Turner, how to design APIs, jason turner cppcon, jason turner c++, cppcon 2022 talks, cppcon back to basics, c++ api development, c++ api design best practices, cpp talk, c++, cpp, cppcon, cpp con, c++ con, cppcon 2022, cppcon back to basics 2022, API design, cpp API, back to basics cppcon, c++ api
Id: zL-vn_pGGgY
Channel Id: undefined
Length: 60min 41sec (3641 seconds)
Published: Sat Dec 03 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.