Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / algorithm

Execute Member Function If It Is Present

4.94/5 (7 votes)
12 Sep 2015MIT1 min read 14.3K   20  
Using C++ templates? Wish your template algorithm will be versatile and ready to work with the classes, which do not fully support the required interface? Want more functional programming with C++? Get it now!

Introduction

I had encountered this problem when working with strings. My class supports the hash code generation, but the std::string is not, while the template algorithm benefits on it. What to do? The solution is SFINAE.

Background

SFINAE - Substitution Failure Is Not An Error.

This rule applies during overload resolution of function templates: When substituting the deduced type for the template parameter fails, the specialization is discarded from the overload set instead of causing a compile error.

This feature is used in template metaprogramming.

Using the Code

The header module itself: (I called it "FuncUtils.h"):

C++
#ifndef FuncUtilsH
#define FuncUtilsH

#include <utility>

#define EXEC_MEMBER_PROC_IF_PRESENT(ProcName) namespace ProcName {\
  void ExecIfPresent(...) throw() {}\
  \
  template <class C, typename... TArgs>\
  void ExecIfPresent(C& obj, TArgs&... args) {\
    obj.ProcName(std::forward<TArgs>(args)...);\
  }\
};

// If NOT exists - returns the 'DefaultValue'
// 'DefaultValue' SHOULD be the same type as a decltype(*.FuncName())
// Works with static/const/virtual funcs
#define EXEC_MEMBER_FUNC_IF_PRESENT(FuncName, DefaultValue) namespace FuncName {\
  template <typename TReturnType = decltype(DefaultValue)>\
  auto ExecIfPresent(...) -> TReturnType {\
    return std::move(DefaultValue);\
  }\
  \
  template <class C, typename... TArgs>\
  auto ExecIfPresent(C& obj, TArgs&... args)\
   -> decltype(obj.FuncName(std::forward<TArgs>(args)...))\
  {/* do NOT use 'const C& obj' NOR 'C::FuncName()!'*/\
    return std::move(obj.FuncName(std::forward<TArgs>(args)...));\
  }\
};

#endif // FuncUtilsH

To use it, just "instantiate" a macro, providing function name and default value (which can be a constant, a literal, a reference, a class member, etc.):

C++
EXEC_MEMBER_FUNC_IF_PRESENT(getHashIfKnown, size_t())

Calling:

C++
const auto hash = getHashIfKnown::ExecIfPresent(str);

Points of Interest

This module is just a small part of the library, which uses C++11 features and which I am working under now, I decided to make it a public property.

History

Update 1

Test code for Ideone online compiler:

C++
// <Include the above code>

// While my MS VS Community 2013 Update 5 ok with 'size_t()' as a default val.,
//  the Ideone wouldn't compile it, so changed to '0U' literal
EXEC_MEMBER_FUNC_IF_PRESENT(getHashIfKnown, 0U);

#include<cassert>
#include<cstring>
#include<string>
#include<iostream>

int main() {
 std::string str = "test str";
 auto hash = getHashIfKnown::ExecIfPresent(str);
 assert(!hash);
 std::cout << "str: " << hash << std::endl;
 
 struct MyStr {
     MyStr(const char* str) throw() {
         if(str) {
             strcpy(buf, str);
             len = strlen(buf);
         }
     }
     
     size_t getHashIfKnown() const throw() {
         size_t hash = 0U;
         for(size_t idx = 0U; idx < len; ++idx) {
             hash += buf[idx] * (idx + 1U);
         }
         return hash;
     }
     
     size_t len = 0U;
     char buf[256U];
 } myStr = "we don't need no water!";
 
 hash = getHashIfKnown::ExecIfPresent(myStr);
 assert(hash);
 std::cout << "myStr: " << hash << std::endl;
}

Output:

str: 0
myStr: 24355

Update 2

GitHub repository link added.

Update 3

Downloadable archive is replaced with the new fixed, updated AND redesigned version 1.01.

Note that actual changes made are NOT described NOR even mentioned in the article's text (which is represented in the original version), you can see them in the GitHub repository:

License

This article, along with any associated source code and files, is licensed under The MIT License