APIs

Application Programming Interfaces, which deal with how other programs interact with your program and vice versa.

  • This means having functions from other programs that are called by functions in our program

They hide implementation: you just use them and they work. We don’t need to read its code to understand it.

  • This lets us use modules / libraries / services w/o knowing the full details with how it works

They usually let you work with software modules, libraries, local/remote computer systems and internet-based applications

  • Good API
    • According to Google’s experts, a good API should be:
      • Easy to learn and use
      • Difficult to use incorrectly
      • Easy to maintain (the code in it is readable and well written)
      • Easy to extend and improve
      • Suitable for those who will be using it
    • For documentation, consider that it
      • Is easy to find
      • Is cleanly organized
      • Is concise
      • Defines all terms
      • Contains examples!
  • In C, API’s are function declarations (inside of a .h header file) alongside other constants and whatnots + their documentation (and maybe even extra external documentation!)
    • This means all the .h files we’ve been using are API’s.

Example: Let’s make an API that deletes a node!

before: (i is the key)

#define N 10
int* deleteNode(int adjMat[N][N], int key);

after:

// Now this does NOT have a fixed size
// it returns 1 or 0 for whether we deleted good or nah 
int deleteNode(int *adjMat, int size, int i)

…but what if we want to use an adjList instead!?

// Graph could either be a matrix or head of a linked list
// The type determines if we're using the matrix or linked list
// this flipping sucks
int deleteNode(void* graph, int size, int i, int type);

…b-but what if we want to use weighted edges!?

This highlights two issues: Who owns/creates the adjacency matrices/lists, and how do we deal with small changes?

  • Thinking abt API design
    • Often you’ll have a function that does 1 task quite well, but a user may want a slightly different variation of said function, so you’d update it to accommodate their needs. However this issue may keep occurring over and over as people ask for reasonably common yet different implementations.
    • ”However, adding functions that are small variations of each other is not a good solution: it creates a lot of code duplication since the functions have the same task, but they work on slight variations of the input which means a lot of the code in each function will be identical and yet not easily separable into smaller, independent functions. It also makes the API cumbersome since developers now have to worry about figuring out which among the many similar functions they should be using.”
    • And then imagine there’s a bug in one of these implementations. Then we’d have to update all of them, and create — possibly — even more bugs.
      • Updating a module w/o changing its declaration will not change how programs use it, but they will have to recompile their modules to use our updated API
  • Once an API is released, it comes really hard to update it
    • If our program has x behavior, but we forgot to add y behavior and it means changing the function declaration, everyone would have to change their programs to accommodate.
    • if u just create a new, separate function, now we’ve run into the code-duplication thing!
    • Maybe instead of updating our module, we show devs how to update our module for their purposes, but this has 2 drawbacks:
      1. They won’t be able to get API updates
      2. If you have a big project, and other modules use that original API you’ve modified, the project might not compile because of disagreements between the two API versions
        1. There are “band-aid solutions” as paco says, but they are not always feasible

OOP

  • Problem: We have to expose our implementation of linked lists for our StringLists API, letting users do crazy things we don’t want them to, cuz our API will do it the right way. How do we stop them from accessing this sensitive information?! “Information Hiding”, yah!
  • And OOP is built around encapsulation, and therefore information hiding

Encapsulation:

  • The wrapping together of components required for some functionality; extends the idea of CDT’s (it can also include code and not just data)
  • Everything related to some module should be encapsulated
  • OOP makes this easy

Information Hiding:

  • Done VIA encapsulation
  • Lets the designer of the API choose what functionality or data should be visible or not.
  • OOP languages let you determine this via private or public keywords, for instance

Classes

  • An entity will all the bundled information we want
  • All components of classes are “member variables” and functions in classes are “class methods”
  • Each member has one of at least 2 access control modifiers in OOP:
    • Public: You can access the member variable anywhere else in code
    • Private: the member variable can only be accessed by class methods (no external program can set/access/use any private data/methods)
  • Have constructors and destructors
    • A constructor is a method automatically called when we create a new instance of a class
      • it initializes things
    • The destructor is automatically called when a class instance goes out of scope or when we explicitly delete it.
      • It cleans up after the class: usually memory-related things
      • A class that implements a linked list would definitely like to clear the linked list instead of creating memory leaks! Objects
  • Are instances of classes; classes create the blueprint of objects
  • Classes are similar to CDTs except no theyre not CDTs are way worse

C++

  • Finally a good language (insert some sarcastic gif here)
  • Unlike C, it supports OOP far better

Lecture things

  • Modules have a .h file and a .c file
    • Headers have the function declarations
    • Code reuse!
      • The reason including a .c is bad practice is that it might include a bunch of extra stuff you don’t need. Perhaps that .c file includes 35 modules… that’s a lot!
      • Instead, you just need to know what functions exist via that header. When you compile, when we link, the program will ONLY include the implementations we actually neeeed.

Tutorial

  • We generalize the synth app

Task 1:

- We store the time index, feeq of note
- length of note
	- In A2 they were the same length but that made no sense

This seems to be it!

typedef struct Note 
{
	float time;
	float freq;
	float length;
	int instrument;
	float volume;
	struct Note *next;
} Note;

Task 2:

  • Generate the sound of the note
void play_note(*Note note)
{
	if (note == NUKL)
	{
		printf("hey no note");
		return;
	}
	
	if (note->instrument == 0) {
		play_piano(note->freq, note->length, note->volume);
		return;
	}
	else
	{
		...
	}
	// keep doing this for all 63 instruments 
	else {
		printf("No note!");
	}
}
  • Problem is, it’s hard to maintain / change and very very long.

(Task 2: Sample Solution 2)

  • instead, each note would have its own sound-generation function.
  • YOU CAN STORE FUNCTION POINTERS?
  • void (*get_sound_sample)(double, double, double);

Task 3

  • Design a CDT for a linked list / BST that contains data for an item as well as functions to handle said structure.
    1. Not a specific problem
    2. Pseudocode is good
    3. List the data + functions specifying what the input and return types are
    4. Code for the functions is NOT in the CDT!
    5. (purpose is to get comfy with the idea that functions can be bundled too!) Linked List Example:
Data:
	int data_value;
	__ *next;
Functions:
	void insert(int x);	
	void delete(int x);
	void sort()

MY example: We do a BST for paths in an interactive novel?

Data:
	char[STR_LEN] dialogue;
	int path_id;
	__ *left;
	__ *right; 
Functions:
	void insert(char[STR_LEN] dialogue, int path_id);
	void delete(int path_id);
	char[STR_LEN] find_dialogue_of_path(int path_id);