First Class Functions

An introduction to first class functions - a language feature that unlocks many powerful design options
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

DreamShaper_v7_fantasy_female_pirate_Sidecut_hair_black_clothi_3.jpg
Ryan McCombe
Ryan McCombe
Posted

Much of the content of this series relates to a programming concept called first-class functions.

A programming language supports first-class functions if its functions can be treated the same way as other types of data, like integers and booleans. For example:

  • Functions can be stored as variables, including in data structures such as class objects
  • Functions can be passed as arguments to other functions
  • Functions can be returned from other functions

C++ supports first-class functions in several different ways. In this chapter, we will cover the three main options we have: function pointers, function objects, and lambdas

But first, let's see a scenario where first-class functions are a useful feature

A Motivating Example

Let’s imagine we have a Party class, which is what enables our characters to group up and take on bigger challenges.

class Party {
public:
  Character* PlayerOne;
  Character* PlayerTwo;
  Character* PlayerThree;
  Character* PlayerFour;
};

Elsewhere in our code, we’ll want to find out more information about this party. For example, we might want to check that everyone is alive:

bool isEveryoneAlive {
  MyParty.PlayerOne->isAlive() &&
  MyParty.PlayerTwo->isAlive() &&
  MyParty.PlayerThree->isAlive() &&
  MyParty.PlayerFour->isAlive()
};

Aside from requiring quite a lot of code, this implementation also makes a big assumption - the party will always have exactly 4 members. In reality, if a party has less, this code may throw an exception due to one of the characters being a nullptr.

Worse, if we later expand our Party class to allow additional characters, code like this may still compile, but return the incorrect result because it’s not checking if the new PlayerFive is alive.

Creating a Class Method

An immediate solution probably comes to mind - we can just add a function to our class to handle this:

class Party {
public:
  bool isEveryoneAlive  {
    return (
      MyParty.PlayerOne->isAlive() &&
      MyParty.PlayerTwo->isAlive() &&
      MyParty.PlayerThree->isAlive() &&
      MyParty.PlayerFour->isAlive()
    );
  };
  Character* PlayerOne;
  Character* PlayerTwo;
  Character* PlayerThree;
  Character* PlayerFour;
};

Now, our code can just call MyParty.isEveryoneAlive()

That is better, but we’ve only really solved one specific use case. There are many possible questions people will want to ask of our party. For example:

MyParty.isEveryoneOnline();
MyParty.isEveryoneAtLeastLevel(50);
MyParty.isAnyoneAHealer();

We can’t predict all the future possibilities, and we wouldn’t want to need to write code for all of those anyway. Thankfully, this is one of the problems first-class functions were designed to solve.

First Class Functions to the Rescue

To solve this problem with first-class functions, let's create a function that implements the actual check we want to do on each member of the party:

bool isAlive(Character* Character) {
  return Character->isAlive();
}

To run this check on everyone in the party, we’d like some way to pass this function to a function on the Party class. A method on our party class that checks if every character meets some condition could be called every. So, our function call might look something like this:

bool isEveryoneAlive { MyParty.every(isAlive) };

The beauty of this design is that we can now ask anything of our Party, without the class needing to be expanded or modified:

bool canStartQuest {
  MyParty.every(isAlive) &&
  MyParty.every(isOnline) &&
  MyParty.every(isAtLeastLevel50) &&
  MyParty.every(isInTheShire)
}

bool isAlive(Character* PartyMember) {
  return PartyMember->isAlive();
}

bool isOnline(Character* PartyMember) {
  return PartyMember->isOnline();
}

bool isAtLeastLevel50(Character* PartyMember) {
  return PartyMember->GetLevel() >= 50;
}

bool isInTheShire(Character* PartyMember) {
  return PartyMember->GetLocation() == LOCATIONS::SHIRE;
}

Over the coming lessons, we will see three ways code like this can be implemented in C++

In the next lesson, we will introduce the first of these: function pointers.

Was this lesson useful?

Ryan McCombe
Ryan McCombe
Posted
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

7a.jpg
This lesson is part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access!

This course includes:

  • 106 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

Function Pointers

An introduction to function pointers, which allow us to store functions in variables, and pass them around like any other data type
d.jpg
Contact|Privacy Policy|Terms of Use
Copyright © 2023 - All Rights Reserved