Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

aliasing a std template function

I need to alias std::get function in order to improve readability in my code.

Unfortunately I got a compile-time error get<0> in namespace ‘std’ does not name a type. using is equivalent to typedef so it needs types to work with. I am using a std::tuple to represent some data type:

using myFoo = std::tuple<int,int,double,string>;
using getNumber = std::get<0>;

I look at some previous questions but the solution proposed is to wrap and use std::forward. I don't want to write such code for each member.

  • Q1 from SO
  • Q2 from SO

Is there a way to get around this using only using keyword?

like image 511
chedy najjar Avatar asked Jan 15 '17 10:01

chedy najjar


People also ask

What is function aliasing?

Function aliases of this type provide natural-language descriptions and prompting for input parameters to an underlying function rule. You need to create and test the function before defining a function alias rule that references it. You cannot define an alias for a function that returns a complex Java type.

What is alias declaration in C++?

You can use an alias declaration to declare a name to use as a synonym for a previously declared type. (This mechanism is also referred to informally as a type alias). You can also use this mechanism to create an alias template, which can be useful for custom allocators.

What is type aliasing?

Type aliases Type aliases provide alternative names for existing types. If the type name is too long you can introduce a different shorter name and use the new one instead. It's useful to shorten long generic types. For instance, it's often tempting to shrink collection types: typealias NodeSet = Set<Network.

What is a templated function?

Function templates are special functions that can operate with generic types. This allows us to create a function template whose functionality can be adapted to more than one type or class without repeating the entire code for each type. In C++ this can be achieved using template parameters.


2 Answers

is there a way to get around this using only using keyword?

I would say no, for std::get is not a type (thus it's not eligible for such an use).
Moreover, even if it was possible, note that std::get is an overloaded function, thus you would have been required to bind yourself to a specific implementation.

That said, in C++17, you can do something like this:

#include<tuple>
#include<utility>

using myFoo = std::tuple<int,int,double>;
constexpr auto getNumber = [](auto &&t) constexpr -> decltype(auto) { return std::get<0>(std::forward<decltype(t)>(t)); };

template<int> struct S {};

int main() {
    constexpr myFoo t{0,0,0.};
    S<getNumber(t)> s{};
    (void)s;
}

As you can see, constexpr lambdas and variables help you creating compile-time (let me say) wrappers you can use to rename functions.


As correctly pointed out by @T.C. in the comments, if you want to generalize it even more and get an almost perfect alias for std::get, you can use a variable template:

template<int N>
constexpr auto getFromPosition = [](auto &&t) constexpr -> decltype(auto) { return std::get<N>(std::forward<decltype(t)>(t)); };

Now you can invoke it as it follows:

S<getFromPosition<0>(t)> s{};

See it on wandbox.

like image 190
skypjack Avatar answered Oct 17 '22 06:10

skypjack


You can do it with a using + an enum:

#include<tuple>

using myFoo = std::tuple<int,int,double>;

int main() {
    constexpr myFoo t{0,0,0.};
    enum { Number = 0 };
    using std::get;

    auto&& x = get<Number>(t);
    (void)x;
}

Although unfortunately, this is not DRY since you have to maintain an enum and a tuple simultaneously.

In my view, the most DRY and safest way to achieve this is to use tagged values in the tuple. Limit the tuple to maximum one of each tag type.

The tag is essentially a mnemonic for some unique concept:

#include <tuple>
#include <iostream>

//
// simple example of a tagged value class
//
template<class Type, class Tag>
struct tagged
{
    constexpr tagged(Type t)
        : value_(t) {}

    operator Type&() { return value_; }

    operator Type const&() const { return value_; }

    Type value_;
};

struct age_tag {};
struct weight_tag {};
struct height_tag {};

using Age = tagged<int, age_tag>;
using Weight = tagged<int, weight_tag>;
using Height = tagged<double, height_tag>;

int main()
{
    constexpr auto foo1 = std::make_tuple(Age(21), Weight(150), Height(165.5));
    constexpr auto foo2 = std::make_tuple(Weight(150), Height(165.5), Age(21));
    using std::get;

    //
    // note below how order now makes no difference
    //

    std::cout << get<Age>(foo1) << std::endl;
    std::cout << get<Weight>(foo1) << std::endl;
    std::cout << get<Height>(foo1) << std::endl;

    std::cout << "\n";

    std::cout << get<Age>(foo2) << std::endl;
    std::cout << get<Weight>(foo2) << std::endl;
    std::cout << get<Height>(foo2) << std::endl;
}

expected output:

21
150
165.5

21
150
165.5
like image 23
Richard Hodges Avatar answered Oct 17 '22 06:10

Richard Hodges