3
votes

The following code:

typedef void HELPER;

const HELPER* helper = _helper;

inline ostream& operator <<(ostream& out,  const HELPER* arg) 
{ out << (const char*)(arg); return out; }

Blows up if I attempt a

cout << helper;

Specifically, I get:

main.cpp:35:28: error: use of overloaded operator '<<' is ambiguous (with operand types 'basic_ostream >' and 'const HELPER *' (aka 'const void *'))

and it lists a few candidates:

/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/ostream:207:0: note: candidate function
    basic_ostream& operator<<(const void* __p);
                   ^
main.cpp:25:17: note: candidate function
inline ostream& operator <<(ostream& out,  const HELPER* arg) 
                ^
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/ostream:195:20: note: candidate function
    basic_ostream& operator<<(bool __n);
                   ^

I'm a little surprised that my typedef isn't invoking a stronger type matching here. How can I get this operator overload running?

EDIT: Further clarification, the purpose of this code is that I am dual-targeting a set of Arduino libraries. They manage their strings frequently with:

typedef void __FlashStringHelper;

void showHelp(const __FlashStringHelper* helpText)
{
   Serial.print(helpText);
}

I like iostream and planned on this dual target, so I overloaded << on Serial object and made the previous into (this is the oversimplified version, for example)

#define cout Serial

void showHelp(const __FlashStringHelper* helpText)
{
   cout << helpText;
}

Now I want to actually target real iostream for a different arch, but the old Arduino code can't vary (much) from its __FlashStringHelpers. That's where I'm at

2
Your symbol HELPER is basically just an alias for void. So you have two overloads for const void* (yours and the standard) plus that pointers can implicitly be converted to a bool giving you a third alternative. - Some programmer dude
typedef doesn't introduce new type. - Danh
And what is the actual problem you want to solve with your shown solution? Why have you made such an overload? What is its purpose? Maybe we can help you with that instead? Please see some related reading about the XY problem. - Some programmer dude
Thanks to all, it is now working! - Malachi

2 Answers

3
votes

typedef doesn't create types it aliases them,

inline ostream& operator <<(ostream& out,  const HELPER* arg) 

is equivalent to

inline ostream& operator <<(ostream& out,  const void* arg)

Maybe you wanted to create a type named HELPER

class HELPER{};
0
votes

As Zekian answered your question this here is something that may be of use to you or help you to achieve what you are trying to do.

#include <iostream>

template <class T>
class Helper {
private:
    T obj_;

public:
    explicit Helper<T>( T obj ) : obj_(obj) {}

public:
    T getObj() const { return obj_; }
    void setObj( T obj ) { obj_ = obj; }        

    template<class U>
    inline friend std::ostream& operator<< ( std::ostream& out, const Helper<U>& rhs ); 
};

template<class U>
std::ostream& operator<< ( std::ostream& out, const Helper<U>& rhs ) {
    return out << rhs.obj_;
}

int main() {
    Helper<int> helper( 3 );
    std::cout << helper << std::endl;
    return 0;
}

It is a wrapper class template with an overloaded ostream operator<<. This will work for integral and atomic types. If you pass another struct or class object then you will have to define other overloaded ostream operators for them.

Example - Same Class Template, but this time using a class or struct.

#include <iostream>

template <class T>
class Helper {
private:
    T obj_;

public:
    explicit Helper<T>( T obj ) : obj_(obj) {}

public:
    T getObj() const { return obj_; }
    void setObj( T obj ) { obj_ = obj; }

    template<class U>
    inline friend std::ostream& operator<< ( std::ostream& out, const Helper<U>& rhs ); 
};

template<class U>
std::ostream& operator<< ( std::ostream& out, const Helper<U>& rhs ) {
    return out << rhs.obj_;
}

struct Staff {
    int employees = 4; // Default to 4
};

int main() {
    Staff staff; 
    Helper<Staff> helper( staff );
    std::cout << helper << std::endl; // will not compile

    return 0;
}

To fix this ostream will need an overloaded operator for Staff object

template <class T>
class Helper {
private:
    T obj_;

public:
    explicit Helper<T>( T obj ) : obj_(obj) {}

public:
    T getObj() const { return obj_; }
    void setObj( T obj ) { obj_ = obj; }

    template<class U>
    inline friend std::ostream& operator<< ( std::ostream& out, const Helper<U>& rhs );
};

template<class U>
std::ostream& operator<< ( std::ostream& out, const Helper<U>& rhs ) {
    return out << rhs.obj_;
}

struct Staff {
    int employees = 4;

    inline friend std::ostream& operator<< ( std::ostream& out, const Staff& rhs );
};

std::ostream& operator<<( std::ostream& out, const Staff& rhs ) {
    return out << rhs.employees;
}

int main() {

    Staff staff;
    Helper<Staff> helper( staff );   // Default to 4
    std::cout << helper << std::endl; // Will Print 4
    // To Change Staff's Employee count for the helper wrapper do this:
    staff.employees = 12; // Change To 12
    helper.setObj( staff ); // pass the changed struct back into helper
    std::cout << helper3 << std::endl; // Will Now Print 12

    // And For Other Default Types
    Helper<int> helper2( 3 );
    std::cout << helper2 << std::endl;

    Helper<float> helper3( 2.4f );
    std::cout << helper3 << std::endl;

    return 0;
}