Reputation: 70287
In Scala, there's a design pattern often called "pimp my library". The basic idea is that we have some class Foo
(presumably in some library that we can't modify), and we want Foo
to act like it has some method or behavior frobnicate
, we can use an implicit class to add the method after the fact.
implicit class Bar(val foo: Foo) extends AnyVal {
def frobnicate(): Unit = {
// Something really cool happens here ...
}
}
Then, if we have an instance of Foo
, we can call frobnicate
on it and, as long as Bar
is in scope, the Scala compiler will be smart enough to implicitly cast Foo
to Bar
.
val foo = new Foo()
foo.frobnicate() // Correctly calls new Bar(foo).frobnicate()
I'd like to do this same sort of thing in C++. I know that C++ has implicit casts, but they don't seem to trigger when accessing members. For a concrete example, the following code in C++ produces an error.
class Foo {}; // Assume we can't modify this class Foo
class Bar {
private:
Foo foo;
public:
Bar(Foo foo) : foo(foo) {}
void frobnicate() {
cout << "Frobnicating :)" << endl;
}
};
void frobnicate(Bar bar) {
cout << "Frobnicating :)" << endl;
}
int main() {
Foo value;
frobnicate(value); // This works
value.frobnicate(); // But this doesn't
return 0;
}
On the value.frobnicate()
line, C++ doesn't seem to look for implicit conversions in that context.
main.cc:30:9: error: ‘class Foo’ has no member named ‘frobnicate’
Note: I'm compiling with C++11 right now. At a cursory glance at the newer C++ versions, it doesn't seem like anything that would affect this question has changed since then. C++11-compatible solutions are ideal, but a way to do this in newer C++ versions would be good as well, for didactic purposes.
Upvotes: 4
Views: 458
Reputation: 4082
Not exactly, but...
#include <iostream>
class Foo
{
public:
void nothingUpMySleeve()
{
std::cout << "Foo!\n";
}
};
class Bar
{
private:
Foo & foo;
public:
Bar(Foo & foo) : foo { foo }
{
}
void frobnicate()
{
std::cout << "Frobnicating\n";
}
Foo * operator ->()
{
return &this->foo;
}
operator Foo &()
{
return this->foo;
}
};
void thisFunctionOnlyAcceptsFoo(Foo & foo)
{
std::cout << "Yes, that's a Foo\n";
}
void useExtendedType(Bar wrapper)
{
// Use the extension function
wrapper.frobnicate();
// Use arrow notation to use the original functions
wrapper->nothingUpMySleeve();
// Implicit conversion kicks in when necessary
thisFunctionOnlyAcceptsFoo(wrapper);
}
int main()
{
Foo value;
// Pass to a function that uses Bar
useExtendedType(value);
}
I really don't recommend doing this in actual code though. It's just a cheap party trick.
It's not even clear whether Bar
should own a copy of Foo
or just reference it. I opted for just referencing because I'm presuming some other code is managing the lifetime of Foo
, and this way Bar
can be created and destroyed incredibly cheaply. You just have to be careful that Bar
's lifetime is always shorter than Foo
's, otherwise it's nasal demons I'm afraid.
Upvotes: 1
Reputation:
What you are describing is called Unified Function Call Syntax (UFCS), and cannot be done in C++ as of today. Anyone who has worked with ranges and algorithm chaining in D can attest to how amazing that would be to have.
The lack of UFCS support is actually one of the main reason why the general modern wisdom is to use free-floating functions and ADL for anything that does not need to be virtual or encapsulation.
There has been proposals to bring D's UFCS to the language, but it's still just a proposal:
https://isocpp.org/files/papers/N4165.pdf
Edit: For anyone wondering why we would ever want that feature, imagine being able to write the following:
std::vector<int> foo(const std::vector<int>& v) {
return v.filter([](int x) {return x > 5;})
.map([](int x) {return x*x;})
.sort();
}
Upvotes: 10
Reputation: 40867
C++ doesn't seem to look for implicit conversions in that context.
That is correct. The mechanism you're looking for doesn't exist in the language.
Upvotes: 1
Reputation: 1040
While you can't do exactly what you are asking you can inherit Foo
and extend it.
class Bar : public Foo{
public:
void frobnicate() {
cout << "Frobnicating :)" << endl;
}
}
now Bar
has everything Foo
has plus the new method. see http://www.cplusplus.com/doc/tutorial/inheritance/
Upvotes: 3