1 00:00:05,300 --> 00:00:08,660 According to Bjarne Stroustrup, the inventor of c++, 2 00:00:09,210 --> 00:00:13,210 generic programming is "writing code that works with a 3 00:00:13,210 --> 00:00:16,870 variety of types as arguments as long as those argument types 4 00:00:16,870 --> 00:00:20,860 meet the specific syntax and semantic requirements." 5 00:00:21,660 --> 00:00:24,260 So why is generic programming such a big deal. 6 00:00:24,810 --> 00:00:28,110 Well, it's because if I have code that's written to be generic, 7 00:00:28,110 --> 00:00:32,910 then I should be able to use any types with that code as long as it makes sense and it should work. 8 00:00:33,910 --> 00:00:36,240 We'll see this specific example later. 9 00:00:36,240 --> 00:00:39,900 But what if i wanted to have a function that took some number as a parameter 10 00:00:39,900 --> 00:00:42,260 and returned the square of that number. 11 00:00:43,020 --> 00:00:45,620 With generic programming, I should be able to pass in 12 00:00:45,620 --> 00:00:47,980 any numeric type into that function, 13 00:00:47,980 --> 00:00:49,980 and it will return the correct result. 14 00:00:50,340 --> 00:00:54,240 So it should work with ints, short ints, longs, long longs, 15 00:00:54,240 --> 00:00:56,740 floats, doubles and so forth, you get the idea. 16 00:00:57,720 --> 00:01:02,320 But we have a bit of a problem. The c++ compiler is statically typed. 17 00:01:02,320 --> 00:01:06,920 So it needs to know the type being passed into that function, 18 00:01:07,320 --> 00:01:09,870 so it can type check it. So if I write the function to expect an integer, 19 00:01:09,870 --> 00:01:12,230 then I won't be able to call it correctly with a double. 20 00:01:12,780 --> 00:01:15,280 So how can we get this to work in c++. 21 00:01:15,830 --> 00:01:19,630 There's a couple of ways. In this video, we'll look at macros. 22 00:01:19,630 --> 00:01:23,630 And in the next several videos, we'll learn about function and class templates. 23 00:01:24,290 --> 00:01:26,490 Notice the note on the macros bullet. 24 00:01:27,040 --> 00:01:30,140 Be very careful when you use macros in practice, 25 00:01:30,140 --> 00:01:32,910 especially macros that use arguments. 26 00:01:33,710 --> 00:01:35,710 So why am I showing it to you then? 27 00:01:36,110 --> 00:01:39,310 Well, sometimes the best way to teach how to do something correctly 28 00:01:39,310 --> 00:01:41,810 is to first teach how to do it in a way that's inferior. 29 00:01:42,610 --> 00:01:46,270 Second, you'll likely see this out there in production code. 30 00:01:46,270 --> 00:01:49,470 There's a ton of legacy c++ code out there 31 00:01:49,470 --> 00:01:51,870 and macros are used in much of that code. 32 00:01:52,370 --> 00:01:55,470 Macros are used more extensively in c code though, 33 00:01:55,470 --> 00:01:58,830 and we sometimes see c code used with c++ code. 34 00:01:59,330 --> 00:02:01,830 Now we've actually already used a macro. 35 00:02:01,830 --> 00:02:05,490 Remember, when we created the include guards around our dot h header files, 36 00:02:05,490 --> 00:02:08,370 we used pound define, well, that's a macro. 37 00:02:08,620 --> 00:02:11,920 In the context of header guards and conditional compilation, 38 00:02:11,920 --> 00:02:16,420 it's okay to use macros. But don't use macros in your code as I'll show you here. 39 00:02:16,920 --> 00:02:19,120 Okay. So let's see how these macros work. 40 00:02:20,480 --> 00:02:22,940 First, the macros begin with a pound sign, 41 00:02:22,940 --> 00:02:27,840 and we know that all directives that begin with the pound sign are preprocessor directives. 42 00:02:27,840 --> 00:02:33,130 And what else do we know? Well, we know that the preprocessor doesn't know c++. 43 00:02:33,330 --> 00:02:36,680 So there is no type information associated with macros. 44 00:02:36,880 --> 00:02:39,880 The preprocessor is just doing simple substitution. 45 00:02:40,980 --> 00:02:46,280 Notice the first pound defined defines the name max size to be 100. 46 00:02:46,640 --> 00:02:50,640 Notice that the 100 has no type associated with, it it's just a 100. 47 00:02:51,640 --> 00:02:56,230 The second pound defined defines the name pi to be 3.14159. 48 00:02:56,890 --> 00:03:00,550 Now if we use these names in our program,the preprocessor will 49 00:03:00,550 --> 00:03:03,910 replace the names with what we defined them to represent. 50 00:03:03,910 --> 00:03:07,210 Notice that there's no semicolon at the end of the macros. 51 00:03:07,210 --> 00:03:11,810 If there was, then the semicolon would also be copied and that's not usually what we want. 52 00:03:13,470 --> 00:03:17,670 In this slide, we can see max size being used in the expression in the if statement, 53 00:03:18,170 --> 00:03:20,470 and pi is being used in a calculation. 54 00:03:21,970 --> 00:03:26,330 The preprocessor will first remove the pound defined statements from our code, 55 00:03:26,630 --> 00:03:30,430 then it goes through the program and replaces any defined names 56 00:03:30,430 --> 00:03:32,230 with what we defined them to be. 57 00:03:32,730 --> 00:03:36,730 You can see the values being replaced here in the if statement and the calculation. 58 00:03:37,330 --> 00:03:39,990 Now the processed file is sent to the compiler. 59 00:03:40,490 --> 00:03:42,150 It looks simple and it is, 60 00:03:42,150 --> 00:03:45,700 but there's a better way to do this and we learned it a long time ago. 61 00:03:45,700 --> 00:03:50,300 We used constants. Constants are typed, and they're known to the compiler. 62 00:03:51,100 --> 00:03:55,000 I know what you're thinking. What does this have to do with generic programming, I'll show you in the next slide. 63 00:03:55,800 --> 00:03:58,800 Suppose we want to write a function that expects two integers 64 00:03:58,800 --> 00:04:01,300 and returns the maximum of those two integers. 65 00:04:01,300 --> 00:04:04,900 We could easily write that max function as you can see in this slide. 66 00:04:05,300 --> 00:04:07,500 Note that we're using the conditional operator. 67 00:04:08,000 --> 00:04:12,200 So it evaluates a greater than b. And if it's true then a is returned, 68 00:04:12,200 --> 00:04:15,450 otherwise b is returned. Simple if else logic. 69 00:04:16,750 --> 00:04:19,110 Now we can define two ints: x and y, 70 00:04:19,110 --> 00:04:22,710 call the max function with x and y as parameters and it works perfectly. 71 00:04:23,310 --> 00:04:25,970 Okay. So what -- where's the generic programming? 72 00:04:26,970 --> 00:04:31,220 Now suppose we need a max function for doubles and another one for characters 73 00:04:31,220 --> 00:04:34,720 and we could have another one for floats and longs, so you get the idea. 74 00:04:34,720 --> 00:04:37,720 We could end up writing many of these max functions. 75 00:04:38,520 --> 00:04:40,820 The code is the same for all of them. 76 00:04:40,820 --> 00:04:43,020 It's that code with the conditional operator. 77 00:04:43,520 --> 00:04:46,520 The only thing that changes is the types of the parameters. 78 00:04:47,400 --> 00:04:49,730 Remember, the idea behind generic programming. 79 00:04:49,730 --> 00:04:54,630 I should be able to use max with any types as long as they make sense. 80 00:04:54,930 --> 00:04:58,920 But how can I do that without writing many versions of this function like we did here. 81 00:04:59,280 --> 00:05:02,280 Well, one way is to use macros with parameters. 82 00:05:03,780 --> 00:05:07,580 In this slide, we're defining a macro named max 83 00:05:07,580 --> 00:05:10,080 that expects two arguments, a and b. 84 00:05:11,080 --> 00:05:15,070 And we can define it to be the conditional expression we saw earlier. 85 00:05:15,070 --> 00:05:18,620 So when the preprocessor sees max with two arguments, 86 00:05:18,620 --> 00:05:21,620 it will replace it with what we defined it in the macro, 87 00:05:21,870 --> 00:05:24,530 and it will also replace the arguments a and b. 88 00:05:25,030 --> 00:05:29,390 So now we have no functions defined, just a single macro called max. 89 00:05:30,390 --> 00:05:33,890 We can now use max in our code with arguments as you see in the examples, 90 00:05:33,890 --> 00:05:34,890 it works fine. 91 00:05:35,390 --> 00:05:37,390 That's an example of generic programming. 92 00:05:37,990 --> 00:05:41,190 Remember, the preprocessor is simply substituting. 93 00:05:41,190 --> 00:05:43,090 It doesn't know c++, 94 00:05:43,090 --> 00:05:47,450 so problems can occur once the compiler tries to compile the processed code. 95 00:05:49,350 --> 00:05:52,950 We have to be very careful with macros. Here's an example. 96 00:05:53,200 --> 00:05:56,100 Suppose we want a simple macro that's defined as 97 00:05:56,100 --> 00:05:59,400 pound defined square with a single argument a, 98 00:05:59,900 --> 00:06:01,900 and it's defined as a times a. 99 00:06:02,560 --> 00:06:06,060 Looks simple, and it is. But there's a few problems here. 100 00:06:06,660 --> 00:06:10,260 Now we can use that macro in result equals square with a 5. 101 00:06:10,510 --> 00:06:14,310 Square 5 will be replaced with 5 times 5. And all is good. 102 00:06:14,560 --> 00:06:16,060 But look at the next example. 103 00:06:16,560 --> 00:06:19,560 Result equals 100 divided by square 5. 104 00:06:20,060 --> 00:06:23,860 What we expect is 100 divided by 25, which is 4, 105 00:06:24,260 --> 00:06:25,860 but that's not what we get. 106 00:06:25,860 --> 00:06:29,060 We get 100 divided by 5 times 5. 107 00:06:29,060 --> 00:06:32,660 Again, blind substitution. So how can we make this better. 108 00:06:34,540 --> 00:06:38,900 When we use macros, we can wrap up all the arguments in parentheses. 109 00:06:38,900 --> 00:06:40,500 This is the best practice. 110 00:06:40,500 --> 00:06:44,100 Now we give ourselves a better chance that the result will be correct. 111 00:06:44,600 --> 00:06:47,200 In this case, we now get what we expect. 112 00:06:47,860 --> 00:06:50,760 Okay. So why don't we want to do this 113 00:06:50,760 --> 00:06:52,660 because the compiler isn't doing it. 114 00:06:52,660 --> 00:06:57,650 The preprocessor is doing it, and it doesn't know the syntax or semantics of c++. 115 00:06:58,530 --> 00:07:02,730 Don't use macros with arguments in your code. If you have legacy code, 116 00:07:02,730 --> 00:07:05,730 understand how it works so that you can better test the code. 117 00:07:06,280 --> 00:07:09,180 So what's a better way to write these max and square functions 118 00:07:09,180 --> 00:07:12,980 so that we can use generic programming and let the compiler do the work. 119 00:07:12,980 --> 00:07:14,480 We can use templates. 120 00:07:14,980 --> 00:07:18,580 In this case, function templates, and we'll learn about them in the next video.