Is it possible to ignore [[nodiscard]] in a special case? - c++

C++17 has a new attribute, [[nodiscard]].
Suppose, that I have a Result struct, which has this attribute:
struct [[nodiscard]] Result {
};
Now, if I call a function which returns Result, I got a warning if I don't check the returned Result:
Result someFunction();
int main() {
someFunction(); // warning here, as I don't check someFunction's return value
}
This program generates:
warning: ignoring return value of function declared with 'nodiscard'
attribute [-Wunused-result]
So far, so good. Now suppose, that I have a special function, for which I still want to return Result, but I don't want this warning generated, if the check is omitted:
Result someNonCriticalFunction();
int main() {
someNonCriticalFunction(); // I don't want to generate a warning here
}
It is because, someNonCriticalFunction() does something non-critical (for example, something like printf - I bet that no-one checks printf's return value all the time); most cases, I don't care if it fails. But I still want it to return Result, as in some rare cases, I do need its Result.
Is it possible to do this somehow?
Possible solutions which I don't like:
I would not like calling it as (void)someNonCriticalFunction(), because this function is called a lot of times, it is awkward
creating a wrapper around someNonCriticalFunction(), which calls (void)someNonCriticalFunction(): I don't want to have a differently named function just because of this
removing [[nodiscard]] from Result, and add it to every function which returns Result

Why not make use of std::ignore from the <tuple> header—that would make the discard explicit:
[[nodiscard]] int MyFunction() { return 42; }
int main()
{
std::ignore = MyFunction();
return 0;
}
Compiler explorer of this code snippet: https://godbolt.org/z/eGPsjajz8
CPP Reference for std::ignore: https://en.cppreference.com/w/cpp/utility/tuple/ignore

I recommend the option you ruled out:
"removing [[nodiscard]] from Result, and add it to every function which returns Result."
But since you don't seem happy with it, here's another solution, using bog-standard inheritance:
struct [[nodiscard]] Result {
};
struct DiscardableResult: public Result {
};
For the functions where you can discard the result, use DiscardableResult as return type:
Result func1();
DiscardableResult func2();
func1(); // will warn
func2(); // will not warn

They say that every problem in computer science can be solved by adding another layer of indirection:
template <bool nodiscard=true>
struct Result;
template <>
struct Result<false> {
// the actual implementation
};
template <>
struct [[nodiscard]] Result<true>
: Result<false>
{
using Result<false>::Result;
};
This is effectively making Result conditionally [[nodiscard]], which allows:
Result<true> someFunction();
Result<false> someNonCriticalFunction();
int main() {
someFunction(); // warning here
someNonCriticalFunction(); // no warning here
}
Although really, this is identical to:
removing [[nodiscard]] from Result, and add it to every function which returns Result
which gets my vote to begin with.

You can suppress the warning with another C++17 attribute, namely [[maybe_unused]]
[[nodiscard]] int MyFunction() { return 42; }
int main()
{
[[maybe_unused]] auto v = MyFunction();
return 0;
}
This way you also avoid the confusing dependency to std::tuple which comes with std::ignore, even CppCoreGuidelines is openly recommending to use std::ignore for ignoring [[nodiscard]] values:
Never cast to (void) to ignore a [[nodiscard]]return value. If you
deliberately want to discard such a result, first think hard about
whether that is really a good idea (there is usually a good reason the
author of the function or of the return type used [[nodiscard]] in the
first place). If you still think it's appropriate and your code
reviewer agrees, use std::ignore = to turn off the warning which is
simple, portable, and easy to grep.
Looking at C++ reference, officially std::ignore is only specified to be used in std::tie when unpacking tuples.
While the behavior of std::ignore outside of std::tie is not formally
specified, some code guides recommend using std::ignore to avoid
warnings from unused return values of [[nodiscard]] functions.

cast the result to a (void *).
int main()
{
(void *)someFunction(); //Warning will be gone.
}
This way you "used" your result as far as the compiler is concerned. Great for when you are using a library where nodiscard has been used and you really don't care to know the result.

Related

How to customize function parameter errors(c++)

I wrote a function that requires two parameters, but I don't want those two parameters to be 0.
I want to make the compiler know that those two parameters cannot be 0 through some ways, otherwise the editor will report an error in the form of "red wavy line".
I refer to "custom exception class" to solve this problem, but I find this method does not work.
If there are someone knows how to do , I will be very happy, because it takes me a whole day
For example:
#include<iostream>
using namespace std;
int Fuction(int i , int j){
//code
}
int main(){
Funciton(1,1);
Funciton(0,0);
//I don't want i or j is zero
//But if they are still zero , The program will still work normally
return 0;
}
There is no integer type without a 0. However, you can provoke a compiler error by introducing a conversion to a pointer type. Its a bit hacky, but achieves what you want (I think) for a literal 0:
#include <iostream>
struct from_int {
int value;
from_int(int value) : value(value) {}
};
struct non_zero {
int value;
non_zero(int*) = delete;
non_zero(from_int f) : value(f.value) {}
};
void bar(non_zero n) {
int i = n.value; // cannot be 0
}
int main() {
bar(non_zero(42));
//bar(non_zero(0)); // compiler error
}
bar is the function that cannot be called with a 0 parameter. 0 can be converted to a pointer but that constructor has no definition. Any other int will pick the other constructor. Though it requires the caller to explicitly construct a non_zero because only one user defined conversion is taken into account.
Note that this only works for a literal 0. There is no error when you pass a 0 to this function:
void moo(int x){
bar(non_zero(x));
}
Thats why it should be considered as a hack. Though, in general it is not possible to trigger a compiler error based on the value of x which is only known at runtime.
If you want to throw an exception, thats a whole different story. You'd simply add a check in the function:
if (i == 0) throw my_custom_exception{"some error message"};
If you are using only MSVC you can also take a look at Structured Annotation Language (SAL). It is described on MSDN.
For your case you might be interested in _In_range_(lb,ub). An example would be:
void f(_In_range_(1,300) int a, _In_range_(1, 2147483647) int b);
Please note that this will not prohibit calling f(0, 0) but code analysis will trigger a warning. That warning will be triggered also in cases where you call f(x,x) and the compiler knows that x is zero.
In the past I liked to use SAL as it makes the interface clearer and can help reveal errors because the compiler can check more semantics. But now with modern C++ und the CppCoreGuidelines I am trying to follow the guidelines and so normally I don't need SAL anymore.

Is there a way to dynamically change the return-type of a function in C++ based on function parameter values?

I am working on a problem that requires me to return different return-types based on my function parameter values that I provide.
I want to do something like this --
In the code below, doSomething() is an already existing function (used by a lot of clients) which takes mode as a function parameter, and returns std::list<ReturnType> already.
Based on the mode value, I had to create another sub-functionality which returns a shared_future<std::list<ReturnType>>.
How can I change this code so that it can return one of the two return types based on the mode value?
Note: ReturnType is a template typename which we are using for the entire class.
Code:
std::shared_future<std::list<ReturnType> > futureValue() {
return functionReturningSharedFuture();
}
std::list<ReturnType> listValue() {
return functionReturningList();
}
std::list<ReturnType> doSomething(int mode) {
if(mode == 1){
// new functionality that I added
return futureValue(); // This (obviously) errors out as of now
}
else{
// already there previously
return listValue();
}
}
int main() {
doSomething(1);
return 0;
}
How can I change this code so that it can return one of the two return types based on the mode value?
Constraints and Issues:
This issue could've been easily solved by function overloading if we provide an extra function parameter (like a true value), but that extra argument is not useful, since we are already using mode. Also, it isn't considered a good design to add variables which have almost no use.
One of the major constraints is that there are clients who are already using this doSomething() expect a std::list<ReturnType>, and so I cannot return boost::any or std::variant or anything similar.
I tried using std::enable_if, but it wasn't working out since we are getting the mode value at runtime.
We can't use template metaprogramming since that would change the way our function is being called on the client-side. Something that we can't afford to do.
Thank you.
This cannot be done.
You can only have one function with a given signature. If you have calling code that already expects this to return a std::list<ReturnType>, that's it; you're done.
If you could guarantee that all existing calling code looks like
auto l = obj.doSomething(1);
then you could potentially change the return type to something which would look like a std::list to any calling code. But if there's any calling code that looks like
std::list<ReturnType> l = obj.doSomething(1);
then that's off the table.
You probably need to rethink your design here.
From the example main, I see doSomething(1);, so maybe at the call site the value of the parameter mode is always known at compile-time. In this case, one option is that you make doSomething a template<int mode> function. I'm thinking about something like this:
#include <iostream>
#include <list>
#include <vector>
// assuming you cannot change this (actually you have changed it in you example, ...)
std::list<int> doSomething(int mode) {
std::cout << "already existing function\n";
return std::list<int>{1,2,3};
}
// then you can put this too
template<int N>
auto doSomething();
template<>
auto doSomething<10>() {
std::cout << "new function\n";
return std::vector<int>{1,2,3};
}
int main() {
auto x = doSomething(3);
auto y = doSomething<10>();
}
Probably another option would be to use a if constexpr intead of if and an auto/decltype(auto) return type in doSomething, but I haven't tried it.

std::experimental::source_location at compile time

std::experimental::source_location will probably be added to the C++ standard at some point. I'm wondering if it possible to get the location information into the compile-time realm. Essentially, I want a function that returns different types when called from different source locations. Something like this, although it doesn't compile because the location object isn't constexpr as it's a function argument:
#include <experimental/source_location>
using namespace std::experimental;
constexpr auto line (const source_location& location = source_location::current())
{
return std::integral_constant<int, location.line()>{};
}
int main()
{
constexpr auto ll = line();
std::cout << ll.value << '\n';
}
This doesn't compile, with a message about
expansion of [...] is not a constant expression
regarding the return std::integral_constant<int, location.line()>{} line. What good it is to have the methods of source_location be constexpr if I can't use them?
As Justin pointed the issue with your code is that function argument are not constexpr but the problem of using source_location in a constexpr function in a more useful way is mentioned in the constexpr! functions proposal which says:
The "Library Fundamentals v. 2" TS contains a "magic" source_location
class get to information similar to the FILE and LINE macros
and the func variable (see N4529 for the current draft, and N4129
for some design notes). Unfortunately, because the "value" of a
source_location is frozen at the point source_location::current() is
invoked, composing code making use of this magic class is tricky:
Typically, a function wanting to track its point of invocation has to
add a defaulted parameter as follows:
void my_log_function(char const *msg,
source_location src_loc
= source_location::current()) {
// ...
}
This idiom ensure that the value of the source_location::current()
invocation is sampled where my_log_function is called instead of where
it is defined.
Immediate (i.e., constexpr!) functions, however, create a clean
separation between the compilation process and the constexpr
evaluation process (see also P0992). Thus, we can make
source_location::current() an immediate function, and wrap it as
needed in other immediate functions: The value produced will
correspond to the source location of the "root" immediate function
call. For example:
constexpr! src_line() {
return source_location::current().line();
}
void some_code() {
std::cout << src_line() << '\n'; // This line number is output.
}
So this is currently an open problem.

How to check if a function exists in C/C++?

Certain situations in my code, I end up invoking the function only if that function is defined, or else I should not. How can I achieve this?
like:
if (function 'sum' exists ) then invoke sum ()
Maybe the other way around to ask this question is how to determine if function is defined at runtime and if so, then invoke?
When you declare 'sum' you could declare it like:
#define SUM_EXISTS
int sum(std::vector<int>& addMeUp) {
...
}
Then when you come to use it you could go:
#ifdef SUM_EXISTS
int result = sum(x);
...
#endif
I'm guessing you're coming from a scripting language where things are all done at runtime. The main thing to remember with C++ is the two phases:
Compile time
Preprocessor runs
template code is turned into real source code
source code is turned in machine code
runtime
the machine code is run
So all the #define and things like that happen at compile time.
....
If you really wanted to do it all at runtime .. you might be interested in using some of the component architecture products out there.
Or maybe a plugin kind of architecture is what you're after.
Using GCC you can:
void func(int argc, char *argv[]) __attribute__((weak)); // weak declaration must always be present
// optional definition:
/*void func(int argc, char *argv[]) {
printf("FOUND THE FUNCTION\n");
for(int aa = 0; aa < argc; aa++){
printf("arg %d = %s \n", aa, argv[aa]);
}
}*/
int main(int argc, char *argv[]) {
if (func){
func(argc, argv);
} else {
printf("did not find the function\n");
}
}
If you uncomment func it will run it otherwise it will print "did not find the function\n".
While other replies are helpful advices (dlsym, function pointers, ...), you cannot compile C++ code referring to a function which does not exist. At minimum, the function has to be declared; if it is not, your code won't compile. If nothing (a compilation unit, some object file, some library) defines the function, the linker would complain (unless it is weak, see below).
But you should really explain why you are asking that. I can't guess, and there is some way to achieve your unstated goal.
Notice that dlsym often requires functions without name mangling, i.e. declared as extern "C".
If coding on Linux with GCC, you might also use the weak function attribute in declarations. The linker would then set undefined weak symbols to null.
addenda
If you are getting the function name from some input, you should be aware that only a subset of functions should be callable that way (if you call an arbitrary function without care, it will crash!) and you'll better explicitly construct that subset. You could then use a std::map, or dlsym (with each function in the subset declared extern "C"). Notice that dlopen with a NULL path gives a handle to the main program, which you should link with -rdynamic to have it work correctly.
You really want to call by their name only a suitably defined subset of functions. For instance, you probably don't want to call this way abort, exit, or fork.
NB. If you know dynamically the signature of the called function, you might want to use libffi to call it.
I suspect that the poster was actually looking for something more along the lines of SFINAE checking/dispatch. With C++ templates, can define to template functions, one which calls the desired function (if it exists) and one that does nothing (if the function does not exist). You can then make the first template depend on the desired function, such that the template is ill-formed when the function does not exist. This is valid because in C++ template substitution failure is not an error (SFINAE), so the compiler will just fall back to the second case (which for instance could do nothing).
See here for an excellent example: Is it possible to write a template to check for a function's existence?
use pointers to functions.
//initialize
typedef void (*PF)();
std::map<std::string, PF> defined_functions;
defined_functions["foo"]=&foo;
defined_functions["bar"]=&bar;
//if defined, invoke it
if(defined_functions.find("foo") != defined_functions.end())
{
defined_functions["foo"]();
}
If you know what library the function you'd like to call is in, then you can use dlsym() and dlerror() to find out whether or not it's there, and what the pointer to the function is.
Edit: I probably wouldn't actually use this approach - instead I would recommend Matiu's solution, as I think it's much better practice. However, dlsym() isn't very well known, so I thought I'd point it out.
You can use #pragma weak for the compilers that support it (see the weak symbol wikipedia entry).
This example and comment is from The Inside Story on Shared Libraries and Dynamic Loading:
#pragma weak debug
extern void debug(void);
void (*debugfunc)(void) = debug;
int main() {
printf(“Hello World\n”);
if (debugfunc) (*debugfunc)();
}
you can use the weak pragma to force the linker to ignore unresolved
symbols [..] the program compiles and links whether or not debug()
is actually defined in any object file. When the symbol remains
undefined, the linker usually replaces its value with 0. So, this
technique can be a useful way for a program to invoke optional code
that does not require recompiling the entire application.
So another way, if you're using c++11 would be to use functors:
You'll need to put this at the start of your file:
#include <functional>
The type of a functor is declared in this format:
std::function< return_type (param1_type, param2_type) >
You could add a variable that holds a functor for sum like this:
std::function<int(const std::vector<int>&)> sum;
To make things easy, let shorten the param type:
using Numbers = const std::vectorn<int>&;
Then you could fill in the functor var with any one of:
A lambda:
sum = [](Numbers x) { return std::accumulate(x.cbegin(), x.cend(), 0); } // std::accumulate comes from #include <numeric>
A function pointer:
int myFunc(Numbers nums) {
int result = 0;
for (int i : nums)
result += i;
return result;
}
sum = &myFunc;
Something that 'bind' has created:
struct Adder {
int startNumber = 6;
int doAdding(Numbers nums) {
int result = 0;
for (int i : nums)
result += i;
return result;
}
};
...
Adder myAdder{2}; // Make an adder that starts at two
sum = std::bind(&Adder::doAdding, myAdder);
Then finally to use it, it's a simple if statement:
if (sum)
return sum(x);
In summary, functors are the new pointer to a function, however they're more versatile. May actually be inlined if the compiler is sure enough, but generally are the same as a function pointer.
When combined with std::bind and lambda's they're quite superior to old style C function pointers.
But remember they work in c++11 and above environments. (Not in C or C++03).
In C++, a modified version of the trick for checking if a member exists should give you what you're looking for, at compile time instead of runtime:
#include <iostream>
#include <type_traits>
namespace
{
template <class T, template <class...> class Test>
struct exists
{
template<class U>
static std::true_type check(Test<U>*);
template<class U>
static std::false_type check(...);
static constexpr bool value = decltype(check<T>(0))::value;
};
template<class U, class = decltype(sum(std::declval<U>(), std::declval<U>()))>
struct sum_test{};
template <class T>
void validate_sum()
{
if constexpr (exists<T, sum_test>::value)
{
std::cout << "sum exists for type " << typeid(T).name() << '\n';
}
else
{
std::cout << "sum does not exist for type " << typeid(T).name() << '\n';
}
}
class A {};
class B {};
void sum(const A& l, const A& r); // we only need to declare the function, not define it
}
int main(int, const char**)
{
validate_sum<A>();
validate_sum<B>();
}
Here's the output using clang:
sum exists for type N12_GLOBAL__N_11AE
sum does not exist for type N12_GLOBAL__N_11BE
I should point out that weird things happened when I used an int instead of A (sum() has to be declared before sum_test for the exists to work, so maybe exists isn't the right name for this). Some kind of template expansion that didn't seem to cause problems when I used A. Gonna guess it's ADL-related.
This answer is for global functions, as a complement to the other answers on testing methods. This answer only applies to global functions.
First, provide a fallback dummy function in a separate namespace. Then determine the return type of the function-call, inside a template parameter. According to the return-type, determine if this is the fallback function or the wanted function.
If you are forbidden to add anything in the namespace of the function, such as the case for std::, then you should use ADL to find the right function in the test.
For example, std::reduce() is part of c++17, but early gcc compilers, which should support c++17, don't define std::reduce(). The following code can detect at compile-time whether or not std::reduce is declared. See it work correctly in both cases, in compile explorer.
#include <numeric>
namespace fallback
{
// fallback
std::false_type reduce(...) { return {}; }
// Depending on
// std::recuce(Iter from, Iter to) -> decltype(*from)
// we know that a call to std::reduce(T*, T*) returns T
template <typename T, typename Ret = decltype(reduce(std::declval<T*>(), std::declval<T*>()))>
using return_of_reduce = Ret;
// Note that due to ADL, std::reduce is called although we don't explicitly call std::reduce().
// This is critical, since we are not allowed to define any of the above inside std::
}
using has_reduce = fallback::return_of_reduce<std::true_type>;
// using has_sum = std::conditional_t<std::is_same_v<fallback::return_of_sum<std::true_type>,
// std::false_type>,
// std::false_type,
// std::true_type>;
#include <iterator>
int main()
{
if constexpr (has_reduce::value)
{
// must have those, so that the compile will find the fallback
// function if the correct one is undefined (even if it never
// generates this code).
using namespace std;
using namespace fallback;
int values[] = {1,2,3};
return reduce(std::begin(values), std::end(values));
}
return -1;
}
In cases, unlike the above example, when you can't control the return-type, you can use other methods, such as std::is_same and std::contitional.
For example, assume you want to test if function int sum(int, int) is declared in the current compilation unit. Create, in a similar fashion, test_sum_ns::return_of_sum. If the function exists, it will be int and std::false_type otherwise (or any other special type you like).
using has_sum = std::conditional_t<std::is_same_v<test_sum_ns::return_of_sum,
std::false_type>,
std::false_type,
std::true_type>;
Then you can use that type:
if constexpr (has_sum::value)
{
int result;
{
using namespace fallback; // limit this only to the call, if possible.
result = sum(1,2);
}
std::cout << "sum(1,2) = " << result << '\n';
}
NOTE: You must have to have using namespace, otherwise the compiler will not find the fallback function inside the if constexpr and will complain. In general, you should avoid using namespace since future changes in the symbols inside the namespace may break your code. In this case there is no other way around it, so at least limit it to the smallest scope possible, as in the above example

Initializing and assigning values,from pass by reference

Okay, this is just a minor caveat. I am currently working with the lovely ArcSDK from ESRI. Now to get a value from any of their functions, you basically have to pass the variable, you want to assign the value to.
E.g.:
long output_width;
IRasterProps->get_Width(&output_width);
Its such a minor thing, but when you have to pick out around 30 different pieces of data from their miscellaneous functions, it really starts to get annoying.
So what i was wondering is it possible to somehow by the magic of STL or C++ change this into:
long output_width = IRasterProps->get_Width(<something magical>);
All of the functions return void, otherwise the off chance some of them might return a HRESULT, which i can safely ignore. Any ideas?
***EDIT****
Heres the final result i got which works :)!!!!!
A magic(P p, R (__stdcall T::*f)(A *)) {
A a;
((*p).*f)(&a);
return a;
}
I know I've already answered, but here's another way. It's better in that it's faster (no boost::function overhead) and avoids the binders (since people seem to have an aversion to them), but is worse in that it's much less general (since it only works for one-argument member functions).
template <typename P, typename T, typename A>
A magic(P p, void (T::*f)(A &)) {
A a;
((*p).*f)(a);
return a;
}
Which you'd call like this:
long output_width = magic(raster_props_object, &IRasterProps::get_Width);
Or, if you happen to be using GCC, we can use some more tricks:
#define MORE_MAGIC(p,f) ({ \
typedef __typeof(*(p)) big_ugly_identifier; \
magic((p),(&big_ugly_identifier::f)); \
})
Which will let us do this:
long output_width = MORE_MAGIC(raster_props_object, get_Width);
(Bonus points if the naming conventions made you think of a PDP-10.)
EDIT: Updated to take any pointer-like type, so it will now work with shared_ptr, iterators, and hopefully _com_ptr.
EDIT: Oops, they're pointers, not references. Here's a version (or overload) that deals with that, and allows -- by ignoring -- arbitrarily-typed return values.
template <typename P, typename T, typename A, typename R>
A magic(P p, R (T::*f)(A *)) {
A a;
((*p).*f)(&a);
return a;
}
This is not quite what you specified because you need to wrap get() around the method, but it works:
template<class T, class S>
T get(S fun(T&)) {
T result;
fun(result);
return result;
}
void foo(int& x) {
x = 5;
}
bool bar(char& x) {
x = 'c';
return false;
}
int main() {
int x = get(foo);
char y = get(bar);
return 0;
}
Can you derive from IRasterProps? Being that the case you can construct your own interface to it.
EDIT: Following on the concept you can probably also apply the Adapter design pattern (or even a Facade if you wish to apply a common interface to several like-minded classes of the SDK).
Looks like a COM object to me.
Visual C++ supports an #import directive to import the type library, and create high-legel wrappers. So you either end up with
width = ptr->GetWidth();
or - even better -
width = ptr->Width;
If a function fails, the HRESULT returned will be transformed into an _com_error exception.
I've used that successfully on many OS and 3rd party COM objects, makes them much easier to use.
Note that you control the wrapper generation through options, the first thing I do is usually adding a rename_namespace or no_namespace, because otherwise the symbold end up in a namespace depending on the typelib name, which is usually ugly.
also, unless you use named_guids option, you might needto change CLSID_xxx and IID_xxx constants to __uuidof(xxx).
EDIT: In retrospect, I'm not sure this one will actually work, since I don't think the template arguments will deduce. Buyer Beware.
Sure! What you need is something to which you can pass a function that will call it and return you the outputted value.
Here's the easy, if less efficient way:
template <typename T>
T magic(boost::function<void(T&)> f) {
T x;
f(x);
return x;
}
Which you'd then call like this using boost::lambda:
long output_width = magic(raster_props_object->*&IRasterProps::get_Width);
Or like this, using boost::bind:
long output_width = magic(bind(&IRasterProps::get_Width, raster_props_object, _1));
You can get rid of boost::function, but that's uglier. Probably worth it, though.
Don't think this is possible. Assigning void to a long should be an error in any case.
Remember, it's probably more performant to pass-by-reference than to return a large object. (won't really make a difference with long's though)
Compiling this:
void foo(long &a) {
}
int main(void) {
long a=0;
a = foo(a);
return 0;
}
gives this error:
g++ x.cc
x.cc: In function ‘int main()’:
x.cc:9: error: void value not ignored as it ought to be
I'm not aware of something insane you could do, precisely like you're asking, and if there was some insane hackery that did work on some peculiar platform I'm pretty sure in a code-review I'd hate it.
It may may more sense to either...
define some trivial inline function wrappers around the APIs you care about
make a specialized class descend from IRasterProps (or whatever) that provides the appropriate accessor methods.
Either of those will impact maintenance time of the code but would safely and cleanly give you the call syntax you are looking for.