Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template argument as function unnamed argument

This question continues Non-static data members class deduction

Here are the unnamed argument functions, that I'm using to return std::string representation of the data type

struct Boo {};
struct Foo {};

std::string class2str(const double) { return "Floating"; };
std::string class2str(const int) { return "Fixed Point"; };
std::string class2str(const Foo) { return "Class Foo"; };
std::string class2str(const Boo) { return "Class Boo"; };

int main(int argc, char* argv[]) 
{
    int    x_a;
    double x_b;
    Foo    F;
    Boo    B;
    std::cout << "x_a     :" << class2str(x_a) << std::endl;
    std::cout << "x_b     :" << class2str(x_b) << std::endl;
    std::cout << "Foo     :" << class2str(F) << std::endl;
    std::cout << "Boo     :" << class2str(B) << std::endl;
};

For type deduction from the non-static member, I am using template:

struct Foo { double A = 33; }

template<typename Class, typename MemType>
std::string class2str(MemType Class::* mData)
{
    return class2str(MemType{}); // Use of empty constructor
}

std::cout << "Foo::A  :" << class2str(&Foo::A) << std::endl;

But this template requires the creation of an object with an empty constructor, which may simply not be there

struct Boo 
{
    double A;
    Boo() = delete;
    Boo(int x) :A(x) {};
};

struct Foo 
{
    double A = 33;
    Boo    b{ 0 };
};

// Compilation error: use of deleted function ‘Boo::Boo()’
std::cout << "Boo::b  :" << class2str(&Foo::b) << std::endl;

How to implement this functionality, but without calling an empty constructor?

See online demo: https://onlinegdb.com/lpc5o8pUKy

like image 397
Konstantin Vinogradov Avatar asked Dec 31 '22 13:12

Konstantin Vinogradov


2 Answers

(As I started writing the answer there was no answer to the question, but as I was about to post it I saw @Jarod42's answer which already show the tag dispatch approach. Posting this answer nonetheless as it uses a slightly different approach of full specializations of a deleted primary template, instead of non-template overloads)


You can use tag dispatch to delegate calls:

#include <iostream>

struct Boo {
  double A;
  Boo() = delete;
  Boo(int x) : A(x){};
};

struct Foo {
  double A = 33;
  Boo b{0};
};

namespace detail {
template <typename T> struct Tag {};

template <typename T> std::string class2str_impl(Tag<T>) = delete;
template <> std::string class2str_impl(Tag<double>) { return "Floating"; };
template <> std::string class2str_impl(Tag<int>) { return "Fixed Point"; };
template <> std::string class2str_impl(Tag<Foo>) { return "Class Foo"; };
template <> std::string class2str_impl(Tag<Boo>) { return "Class Boo"; };

} // namespace detail

template <typename T> std::string class2str(T) {
  return class2str_impl(detail::Tag<T>{});
}

template <typename Class, typename MemType>
std::string class2str(MemType Class::*) {
  return class2str_impl(detail::Tag<MemType>{});
}

int main() {
  int x_a{42};
  double x_b{4.2};
  Foo F{};
  Boo B{x_a};

  std::cout << "x_a     :" << class2str(x_a) << std::endl;
  std::cout << "x_b     :" << class2str(x_b) << std::endl;
  std::cout << "Foo     :" << class2str(F) << std::endl;
  std::cout << "Boo     :" << class2str(B) << std::endl;
  std::cout << "Boo::b  :" << class2str(&Foo::b) << std::endl;
};

where the primary template of class2str_impl may either be deleted (as above), or implement a custom message that a given type does not have a mapped string.

like image 187
dfrib Avatar answered Jan 09 '23 06:01

dfrib


All your overloads currently take object. You might take type instead, or object which hold type:

template <typename T> struct Tag{};

std::string class2str(Tag<double>){ return "Floating";};
std::string class2str(Tag<int>){ return "Fixed Point";};
std::string class2str(Tag<Foo>){ return "Class Foo";};
std::string class2str(Tag<Boo>){ return "Class Boo";};


template<typename Class, typename MemType>
std::string class2str(Tag<MemType Class::*>)
{
    return class2str(Tag<MemType> {});
}

With usage:

int main(int argc, char *argv[]) {
    int    x_a;
    double x_b;
    Foo    F;
    Boo    B;

    std::cout<< "x_a     :" << class2str(Tag<decltype(x_a)>{}) <<std::endl;
    std::cout<< "x_b     :" << class2str(Tag<decltype(x_b)>{}) <<std::endl;
    std::cout<< "Foo     :" << class2str(Tag<decltype(F)>{}) <<std::endl;
    std::cout<< "Boo     :" << class2str(Tag<decltype(B)>{}) <<std::endl;
    // or
    std::cout<< "int     :" << class2str(Tag<int>{}) <<std::endl;
};
like image 38
Jarod42 Avatar answered Jan 09 '23 05:01

Jarod42