Home > other >  Can c std::source_location be used with a conversion operator?
Can c std::source_location be used with a conversion operator?

Time:03-09

I'm trying to add debug instrumentation in C . I want to probe all changes to a variable. To do that, I'm using the c 20 std::source_location::current() feature, like this member function:

void DoubleWithProbe::set(double newValue, std::source_location loc = std::source_location::current())
{
    std::cout << "Changed: new = " << newValue << ", old = " << m_value
        << " (" << loc.file_name() << ":"
        << loc.line() << ")" << std::endl;
    m_value = newValue;
}

However, this requires the caller to use a set function. If I try to use operators, the pattern fails:

operator double(std::source_location loc = std::source_location::current()) const
{
    std::cout << "Accessed: value = " << m_value <<
            << " (" << loc.file_name() << ":"
            << loc.line() << ")" << std::endl;
    return m_value;
}
DoubleWithProbe& operator=(double newValue, std::source_location loc = std::source_location::current())
{
    ...
}

These are both illegal because operator double may not have arguments and operator= must have only one argument. For operator=, I've found this workaround:

    struct DoubleAndLoc
    {
        double m_val;
        std::source_location m_loc;
        // not explicit.
        DoubleAndLoc(double val, std::source_location loc = std::source_location::current())
            : m_val(val), m_loc(loc)
        {
        }
    };
    DoubleWithProbe& operator=(DoubleAndLoc newValue)
    {
        ...
    }

This is probably ok for doubles, but for strings, this would quickly run into double-conversion issues.

Is there any way to make operator double() work, and any way to make operator=() work more cleanly?

A working example is at https://godbolt.org/z/4Ea1sPT1n.

CodePudding user response:

In , no. But in , actually yes, thanks to deducing this. I guess this is yet another discovered use case of that facility (of which I am a coauthor).

The idea of the feature is that you can take your member function like:

struct DoubleWithProbe {
    operator double() const;
};

and rewrite it from having an implicit object parameter to having an explicit object parameter (in this case by convention named self, the this keyword indicates that it is the explicit object parameter):

struct DoubleWithProbe {
    operator double(this DoubleWithProbe const& self);
};

But there's no requirement that this parameter must have the same type as the class that it's in. You could, instead, create a type like this (note that RefWithLoc<T> is implicitly constructible from a T&):

template <typename T>
struct RefWithLoc {
    T& t;
    std::source_location loc;

    RefWithLoc(T& t, std::source_location loc = std::source_location::currenct())
      : t(t)
      , loc(loc)
    { }
};

And then use that as the explicit object parameter type:

struct DoubleWithProbe {
    operator double(this RefWithLoc<DoubleWithProbe const> self);
};

And now, from within the body, self.t is a DoubleWithProbe (a reference to the same object that, in the original implementation, this would have pointed to) but you also get self.loc as desired.

CodePudding user response:

You can generalise your DoubleAndLoc, and then use a member function overloads instead of operator =.

template <typename T>
struct LocatedValue
{
    T m_val;
    std::source_location m_loc;
    // not explicit.
    LocatedValue(T val, std::source_location loc = std::source_location::current())
        : m_val(std::forward<T>(val)), m_loc(loc)
    {
    }
};

class StringWithProbe {
public:
    StringWithProbe& operator=(LocatedValue<const char *> newValue)
    {
        assign(newValue.m_val, newValue.m_loc);
        return *this;
    }
    StringWithProbe& operator=(LocatedValue<std::string> newValue)
    {
        assign(newValue.m_val, newValue.m_loc);
        return *this;
    }

private:
    void assign(const char * value, std::source_location loc)
    {
        //...
    }
    void assign(std::string value, std::source_location loc)
    {
        //...
    }
};

See it on coliru

  • Related