Function Overloading (Compile-Time Polymorphism)

cardimg

In C, every function must have a unique name (e.g., print_int, print_string, print_float).

In C++, Function Overloading allows multiple functions to share the same name, provided they have different parameter lists (number or types of arguments).

The compiler decides which function to call at compile time based on the arguments passed. This is known as Static Resolution.

Syntax & Usage

1. Basic Overloading

You can define multiple versions of a log() function to handle different data types cleanly.

void log(int val) {
    // Code to print integer to UART
}

void log(const char* msg) {
    // Code to print string to UART
}

void log(double val) {
    // Code to print float to UART
}

// Usage
log(100);       // Calls log(int)
log("Error");   // Calls log(const char*)
log(3.14);      // Calls log(double)

2. Overloading by Number of Arguments

Useful for providing default behaviors or extended options.

// Initialize timer with default prescaler
void timer_init(int period_ms);

// Initialize timer with specific prescaler
void timer_init(int period_ms, int prescaler);

How It Works: Name Mangling

C++ supports this by "mangling" the function names. The compiler adds information about the parameters to the internal symbol name.

Function DeclarationInternal Linker Symbol (Example)
void func(int)_Z4funci
void func(double)_Z4funcd
void func(int, int)_Z4funcii

Note: This is why you must use extern "C" when calling C functions from C++ (to stop mangling).

Relevance in Embedded/Firmware

1. Cleaner HAL/Driver APIs

Instead of remembering uart_send_byte, uart_send_array, uart_send_string, you simply use uart.send(). The compiler picks the right driver function for you.

serial.print(10);      // Easy to read
serial.print("Hello"); // Consistent interface

2. Constructor Overloading

A peripheral driver can have multiple constructors to support different initialization modes (e.g., one for default settings, one for custom pins).

// Default I2C (uses standard pins)
I2C_Driver sensor_bus; 

// Remapped I2C (uses custom pins)
I2C_Driver sensor_bus(PB_9, PB_8); 

3. Type-Safe Alternatives to void*

In C, generic handlers often take void* and size, requiring unsafe casting. Overloading lets you write specific, type-safe handlers for int, struct, or float without casting.

Common Pitfalls (Practical Tips)

PitfallDetails
❌ Return Type Only

You cannot overload based only on return type.

int get(); and void get(); will cause a compile error.

❌ Implicit Conversions

Passing 0 (int) to a function expecting a pointer (char*) or double can trigger the wrong overload or ambiguity.

Fix: Use nullptr or explicit casts.

❌ Code Bloat

Remember: Overloaded functions are separate functions in Flash memory. They share a name, not code. If you implement logic 3 times, you use 3x the flash.

Fix: Have all overloads call a single private "worker" function to share logic.

✅ Default ArgumentsSometimes using Default Arguments (void func(int x, int y=0)) is better than overloading if the code logic is identical.
extern "C" ConflictsYou cannot overload functions inside an extern "C" block because C does not support name mangling.

 

 

 

 

Concept understood? Let's apply and learn for real

Practice now