element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Software
  • Products
  • Dev Tools
  • Software
  • More
  • Cancel
Software
Forum C++ and templates: callbacks that return a value, or a void
  • Forum
  • Documents
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Software to participate - click to join for free!
Actions
  • Share
  • More
  • Cancel
Forum Thread Details
  • Replies 0 replies
  • Subscribers 26 subscribers
  • Views 1826 views
  • Users 0 members are here
  • template
  • OO
  • c++
  • callback
Related

C++ and templates: callbacks that return a value, or a void

Jan Cumps
Jan Cumps over 1 year ago

In C(++), a function can return a value. Or a void.
A function with a return value ends with return X;.
A function that returns void, has a last line return;. Without value. (and you don't have to write that return;, but that doesn't matter in this discussion)
When using templates, this needs consideration, if you allow the return type to  be "templated" and you want to support void as one of the options for a return value of a member.

A void function (named a procedure in a lot of programming languages) is different than a function that returns something. Internally and conceptually.
internally: the return value is a position on the stack. void functions don't have that position.
conceptually: ignoring the internally difference, and returning something in a void() function anyways, breaks the C(++) language. Also not returning a value (of the correct type) in a function that does require a return value, fails to compile.

I wrote a templated class that can call a callback. And I wanted it to support returning numbers / bools. 
The thing is: when no callback function is set, the class had to work too. It has to return a value in that case .So I wrote this code:

	inline R call(Args... args) {
		if (_callback == nullptr) {
			return 0; // R can only be a arithmetic type. 0 should work as default.
		}
		return (*_callback)(args...);
	}

This works great for boolean, integer, float return values. But it you want to support callbacks that don't have a return value, it will fail.
If R is type void, the compilation will not allow return 0;. And if R is a number, compilation will not allow return;.

solution: constant expressions

In C++ , we can write constant expressions. A condition that's checked at compile time. If it's true, the containing block will be compiled. If it's false, not.
A bit related to the C style #ifdef , but context aware. A constant expression knows the code it's acting upon (including parameter types). These constant expressions understand C++ source code.
What they have in common with defines: they both are static: they resolve everything during compilation. They have no runtime cost.

I use this construct to have different behaviour if the return type of my callback function is void, and when it's an arithmetic (number) type:

	/*
	 * R can either be an arithmetic type, or void
	 */
	inline R call(Args... args) {
		if constexpr (std::is_void<R>::value) {
			if (_callback == nullptr) {
				return;
			}
			(*_callback)(args...);
		}

		if constexpr (! std::is_void<R>::value) {
			if (_callback == nullptr) {
				return 0; // R can only be a arithmetic type. 0 should work as default.
			}
			return (*_callback)(args...);
		}
	}

You can see that when my templated code is configured for a callback function that returns a bool or a number, it 'll use the lower part, and always return a value.
When the template is configured for a void() type of callback (a procedure), it just returns, without a return value.

This compiles, runs, is type safe and has no runtime impact. if constexpr gets resolved when you build the code. If your code doesn't have void() callbacks, the top part doesn't get compiled. If it doesn't have callbacks that expect a return value, the bottom part doesn't get compiled.

Check my set of test cases below, to see how this works:

#include <cstdio>
#include "callbackmanager.h"
#include <string>

class MyClass {
public:
	inline int handler(const int& num1, const int& num2) const {
          return num1 + num2;
      }
      static inline int staticHandler(const int& num1, const int& num2) {
          return num1 + num2;
      }
};

int functionHandler(const int& num1, const int& num2) {
    return num1 + num2;
}

int main() {

    int a = 4;
    int b = 5;

	{ // scenario: call object method
    MyClass myClass;

    Callback<int, const int&, const int&> cb;
    // Use a lambda to capture myClass and call the object method
    cb.set([&myClass](const int& num1, const int& num2) -> int {
        return myClass.handler(num1, num2);
    });

    int o = cb.call(a, b);
    printf("Value: %i\n", o);
	fflush(stdout);
	}

	{ // scenario: call static method
    Callback<int, const int&, const int&> cb;
    // Use a lambda to call the static method
    cb.set([](const int& num1, const int& num2) -> int {
        return MyClass::staticHandler(num1, num2);
    });

    int o = cb.call(a, b);
    printf("Value: %i\n", o);
	fflush(stdout);
	}

	{ // scenario: call C function
    Callback<int, const int&, const int&> cb;
    // Use a lambda to call the classic C function
    cb.set([](const int& num1, const int& num2) -> int {
        return functionHandler(num1, num2);
    });

    int o = cb.call(a, b);
    printf("Value: %i\n", o);
	fflush(stdout);
	}

	{ // scenario: call pure lambda
    Callback<int, const int&, const int&> cb;
    // Use a lambda to execute anonymous C code
    cb.set([](const int& num1, const int& num2) -> int {
        return num1 + num2;
    });

    int o  = cb.call(a, b);
    printf("Value: %i\n", o);
	fflush(stdout);
	}

	{ // scenario: return a bool
    Callback<bool, const int&, const int&> cb;
    // Use a lambda to execute anonymous C code
    cb.set([](const int& num1, const int& num2) -> bool {
        return num1 == num2;
    });

    printf("Value: %s\n", cb.call(a, b) ? "true" : "false");
	fflush(stdout);
	}

	{ // scenario: use a callback that returns void
    Callback<void, const int&, const int&> cb;
    // Use a lambda to execute anonymous C code
    cb.set([](const int& num1, const int& num2) {
        printf("void gets num1: %i, num2: %i\n", num1, num2);
    	fflush(stdout);
        return;
    });
    cb.call(a, b);
	}

	{ // scenario: use a callback that returns void, and has no attributes
    Callback<void> cb;
    // Use a lambda to execute anonymous C code
    cb.set([]() {
        printf("void with no parameters\n");
    	fflush(stdout);
        return;
    });
    cb.call();
	}

	/*
	{ // scenario: use an unsupported (non-fundamental) type for return value R
	  // this will generate a compile error
    Callback<std::string, const int&, const int&> cb;
	}
*/

}

a constexpr knows "everything" about your code. When you use it in a template class, it will know for what types your project will use that template class. Then it 'll take care that it 'll only send that part of the code to the compiler, that is needed for your project.

For classes like the one that I'm writing, this is great. My class has no clue how it 'll be used. But I provide logic for a set of scenarios that I want to support.
C++ constructs like constexpr will take care that this flexibility has no cost when executing the code. All code gets checked at compilation, and needs to pass the strict C++ type checks. But at no cost for the final firmware.

  • Sign in to reply
  • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube