1

I have structs/class like this:

struct LD {                             //Login detail
        std::string username;
        std::string password;

        std::string toString() const {
        return "Username: " + username
        + " Password: " + password;
    }
};

struct UP {                          //User Profile
    std::string name;
    std::string email;

    ostream& pPrint(ostream& ost) const {
        ost << "Name: " << name
        << " Email: " << email;
        return ost;
    }

    std::string toString() const {
        return "NULLSTRING";
    }
};

I am creating a template pPrint class which will call pPrint function of that class if its present. if not, it will call toString function of that class if its also not available it will print "NO print function"

priority :- 1)pPrint 2)toString 3)simply output "NO print Function"

int main() {

    LD ld = { "And", "Ap" };
    UP  up = { "James Brannd", "jamens@goo.com" };

    // this should print "Name: James Email: jamens@goo.com"
    std::cout << PPrint <UP> (up) << std::endl;

    // this should print "Username: And Password: Ap"
    std::cout << PPrint <LD> (ld) << std::endl;
}

now I have created this class as below:

template<typename T>
struct HaspPrintMethod
{
    template<typename U, std::ostream&(U::*)(std::ostream&) const> struct     SFINAE {};
    template<typename U> static char Test(SFINAE<U, &U::pPrint>*);
    template<typename U> static int Test(...);
    static const bool Has = sizeof(Test<T>(0)) == sizeof(char);
};

template <class T>
class PPrint {
    public:
    PPrint(T m)
    {
        CallPrint(m, std::integral_constant<bool,     HaspPrintMethod<T>::Has>());
    }
    void CallPrint(const T& m, std::true_type)
    {
        std::ostringstream  os;
        m.pPrint(os);
        buf = os.str();
    }
    void CallPrint(const T& m, std::false_type)
    {
        buf = m.toString();
    }
    std::string buf;
};
template <class T>
std::ostream& operator<<(std::ostream &os, pPrint<T> const &m)
{
    return os << m.buf;
}

but it's not working refer: Check if a class has a member function of a given signature

new requirement:- Template Class name is PPrint the function we want to detect is 1)pPrint 2)toString 3)if not this to "No func available"

pPrint with this prototype should be detected:

ostream& pPrint(ostream& ost) const;

but the functions in the structs can be like: (which should not get detected)

ostream& PPrint(ostream& ost) const; // case sensitive and same name as class name
ostream& pPrint(ostream& ost);  //without const specifier 

How to construct Template Class PPrint to do so?

Community
  • 1
  • 1

2 Answers2

2

I think the better way to approach this is via the detection idiom that is being standardized in C++17 as std::is_detected

C++11

First we need some helper structs and type aliases to implement the detection idiom:

template<class...>
using void_t = void;

template<typename T, typename=void_t<>>
struct HaspPrintMethod : std::false_type{};

template<typename T>
struct HaspPrintMethod<T, void_t<decltype(std::declval<T>().pPrint(std::declval<std::ostream&>()))>> : std::true_type{};

template<typename T>
using HaspPrintMethod_t = typename HaspPrintMethod<T>::type;

and also to check for toString:

template<typename T, typename=void_t<>>
struct HasToStringMethod : std::false_type{};

template<typename T>
struct HasToStringMethod<T, void_t<decltype(std::declval<T>().toString())>> : std::true_type{};

template<typename T>
using HasToStringMethod_t = typename HasToStringMethod<T>::type;

And then we simplify the tag-dispatch call:

 pPrint(T m)
 {
     CallPrint(m, HaspPrintMethod_t<T>());
 }

If no pPrint method is available, we'll enter the std::false_type tag, which we then further dispatch:

void CallPrint(const T& m, std::false_type)
{
    CallPrintNopPrint(m, HasToStringMethod_t<T>());
}

private:
void CallPrintNopPrint(const T& m, std::true_type)
{
    buf = m.toString();
}
void CallPrintNopPrint(const T& m, std::false_type)
{
    buf = "NO print Function";
}

Live Demo

Our test:

LD ld = { "And", "Ap" };
UP  up = { "James Brannd", "jamens@goo.com" };

// this should print "Name: James Email: jamens@goo.com"
std::cout << pPrint <UP> (up) << std::endl;

// this should print "Username: And Password: Ap"
std::cout << pPrint <LD> (ld) << std::endl;

// this should print "NO print Function";
struct Foo{};
Foo f;
std::cout << pPrint<Foo>(f) << std::endl;

Output:

Name: James Brannd Email: jamens@goo.com
Username: And Password: Ap
NO print Function

(In fact, I'd probably hide all the CallPrint methods as private because I don't expect the user to call them, but I left the existing ones as-is because that's how OP had them)


C++17

Our detection idiom will use std::is_detected and constexpr if

Demo

(I don't think the compiler supports the [[maybe_unused]] attribute specifier yet, else I'd use it and squash that warning)

template<class T>
using HasPrintMethod = decltype(std::declval<T>().pPrint(std::declval<std::ostream&>()));
template<class T>
using HasToStringMethod = decltype(std::declval<T>().toString());

 // ...
constexpr pPrint(T m)
{
    if constexpr(is_detected<HasPrintMethod, T>::value)
    {
        std::ostringstream  os;
        m.pPrint(os);
        buf = os.str();
    }
    else
    { 
        if constexpr (is_detected<HasToStringMethod, T>::value)
        {
           buf = m.toString();
        }
        else
        {
           buf = "NO print Function";
        }
    }
}
Community
  • 1
  • 1
AndyG
  • 39,700
  • 8
  • 109
  • 143
  • "*priority :- 1)pPrint 2)toString 3)simply output "NO print Function"*" – Piotr Skotnicki Jan 05 '17 at 15:56
  • I want to print "No function available" if both of them are not present how to add this functionality? –  Jan 05 '17 at 16:06
  • only C++11 standards allowed first of all :( –  Jan 05 '17 at 16:07
  • @VenuKantSahu: The C++17 part is for future readers :-). See my edits for the C++11 portion. – AndyG Jan 05 '17 at 16:25
  • @AndyG what's the level of question for a student according to you? –  Jan 05 '17 at 16:29
  • @VenuKantSahu: I suppose I'd consider this more of an expert level question. It's not something you'll come across unless you are really diving into the language. Many C++ colleagues I know would not be able to do this. – AndyG Jan 05 '17 at 16:31
  • @VenuKantSahu: You are using `ostream` without qualifying it under `std::` Either add a `using std::ostream`, or qualify it with the namespace. – AndyG Jan 05 '17 at 16:44
  • applying using namespace std or using std::ostream produces lots of error –  Jan 05 '17 at 16:48
  • @AndyG check once more or edit it if you can just comment what you have edited, –  Jan 05 '17 at 16:52
  • @VenuKantSahu: The `PrettyPrint` function for `Hobbies` should be const: `ostream& PrettyPrint(ostream& ost) const`: http://ideone.com/nMXbpJ – AndyG Jan 05 '17 at 16:56
  • @AndyG any other solution as I can't Change what's inside Hobbies –  Jan 05 '17 at 16:59
  • @AndyG can we do something so that it cant detect function which donot have const ? –  Jan 05 '17 at 17:19
  • @VenuKantSahu: Those functions should be `const`, though. Additionally, you have an odd mix of `prettyPrint` and `PrettyPrint` methods. I'm certain we could have a detection return `false_type` if the method is not `const` if you really need it, though. Edit: They *should* be `const` because the argument itself to `CallPrint` is `const T&`. Discarding the `const` qualifier is a compiler error. – AndyG Jan 05 '17 at 17:43
  • I see that you said you cannot edit what is inside the `struct`s. So you now have a few challenges. Because you have all these combinations: `prettyPrint() const`, `prettyPrint()`, `PrettyPrint()`. Your `class PrettyPrint` will need to adapt to handle all of these, and so will your `Has*` traits. – AndyG Jan 05 '17 at 17:50
  • 1
    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/132425/discussion-between-venukant-sahu-and-andyg). –  Jan 05 '17 at 17:56
1

you can use another integral_constant to get the priority you wish

template <class T>
class pPrint {
    public:


    pPrint(T m)
    {
        CallPrint(m, std::integral_constant<bool, HaspPrintMethod<T>::Has>(), std::integral_constant<bool, HastoString<T>::Has>());
    }

    void CallPrint(const T& m, std::true_type, std::true_type)
    {
        std::ostringstream  os;
        m.pPrint(os);
        buf = os.str();
    }

    void CallPrint(const T& m, std::true_type, std::false_type)
    {
        std::ostringstream  os;
        m.pPrint(os);
        buf = os.str();
    }

    void CallPrint(const T& m, std::false_type, std::true_type)
    {
        buf = m.toString();
    }

    void CallPrint(const T& m, std::false_type, std::false_type)
    {
        buf = "No print function";
    }

    std::string buf;
};

live demo

Biggy Smith
  • 910
  • 7
  • 14