13. Process Sensor Reading

Your firmware processes a sensor reading in two different ways depending on whether the value is allowed to be modified or must be treated as read-only.

You must create two overloaded functions named process that differ only by const-correctness:

  • If a reading is non-negative, the modifying overload must be used.
    • This overload must double the sensor value.
  • If a reading is negative, the read-only overload must be used.
    • This overload must print "readonly" and must not modify the value.

You must use function overloading with const and non-const references.
Do not merge both behaviors into a single function.

In main():

  • Read an integer x
  • If x < 0, call the read-only overload
  • Otherwise, call the modifying overload
  • After modification, print the updated value
     

Example 1

Input:

5

Output:

10

 

Example 2

Input:

-3

Output:

readonly

 

Constraints:

  • Two overloaded versions of the process must exist
  • One overload must accept a non-const reference
  • One overload must accept a const reference

The read-only overload must not modify the input

 


 

Need Help? Refer to the Quick Guide below

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.