I have a series of classes A, B, C, ...
I want to define conversion between any pair of classes.
The conversion has to follow the ordering of classes in the series.
For example, a conversions from A to C has the following steps:
A converts to B.
B finally converts to C
Similarly, a conversion from C to A has the following steps:
C to B
B to A
I try to modify the answer of my last question:
Chained conversion between classes without public inheritances
But I can only get chained conversion from C to A work.
Chained conversions in the opposite direction doesn't work.
How to make chained conversions in both directions work?
Test (also at godbolt.org)
#include <type_traits>
#include <iostream>
template <typename T1, typename T2>
struct conversion_chain {
template <typename T, typename = std::enable_if_t<
std::is_constructible_v<T, T2>
>>
operator T() {
return static_cast<T1 *>(this)->operator T2();
}
};
struct B;
struct C;
struct A { operator B(); };
struct B : conversion_chain<B, A>, conversion_chain<B, C> {
operator A();
operator C();
};
struct C : conversion_chain<C, B> {
operator B();
};
A::operator B() {
std::cout << "A -> B\n";
return B();
}
B::operator A() {
std::cout << "B -> A\n";
return A();
}
B::operator C() {
std::cout << "B -> C\n";
return C();
}
C::operator B() {
std::cout << "C -> B\n";
return B();
}
int main() {
A a = C();
C c = A(); // This line does not compile.
return 0;
}
Compilation error:
<source>:49:11: error: conversion from 'A' to non-scalar type 'C' requested
C c = A(); // This line does not compile.
^~~
Fix by making A inherits from conversion_chain:
struct A : conversion_chain<A, B> { operator B(); };
Related
This question is related to Enforcing a common interface with std::variant without inheritance.
The difference between that question and this one, is that I wouldn't mind inheritance, I am simply looking for the following structs/classes...
struct Parent { virtual int get() = 0; };
struct A : public Parent { int get() { return 1; } };
struct B : public Parent { int get() { return 2; } };
struct C : public Parent { int get() { return 3; } };
... to be AUTOMATICALLY "assembled" into a template:
template<typename PARENT, typename... TYPES>
struct Multi
{
// magic happens here
}
// The type would accept assignment just like an std::variant would...
Multi<Parent, A, B, C> multiA = A();
Multi<Parent, A, B, C> multiB = B();
Multi<Parent, A, B, C> multiC = C();
// And it would also be able to handle virtual dispatch as if it were a Parent*
Multi<Parent, A, B, C> multiB = B();
multiB.get(); // returns 2
Is this possible? If so, how? I would like to avoid working with handling pointers, as the use of std::variant/unions is intended to make memory contiguous.
You can't automagically set this up to allow multiB.get(), but you can allow multiB->get() or (*multiB).get() and even implicit conversion, by providing operator overloads:
template<typename Base, typename... Types>
struct Multi : std::variant<Types...>
{
using std::variant<Types...>::variant;
operator Base&() { return getref<Base>(*this); }
Base& operator*() { return static_cast<Base&>(*this); }
Base* operator->() { return &static_cast<Base&>(*this); }
operator const Base&() const { return getref<const Base>(*this); }
const Base& operator*() const { return static_cast<const Base&>(*this); }
const Base* operator->() const { return &static_cast<const Base&>(*this); }
private:
template<typename T, typename M>
static T& getref(M& m) {
return std::visit([](auto&& x) -> T& { return x; }, m);
}
};
You've probably encountered this kind of thing before when using iterators from the standard library.
Example:
int main()
{
Multi<Parent, A, B, C> multiA = A();
Multi<Parent, A, B, C> multiB = B();
Multi<Parent, A, B, C> multiC = C();
// Dereference
std::cout << (*multiA).get();
std::cout << (*multiB).get();
std::cout << (*multiC).get();
// Indirection
std::cout << multiA->get();
std::cout << multiB->get();
std::cout << multiC->get();
// Implicit conversion
auto fn = [](Parent& p) { std::cout << p.get(); };
fn(multiA);
fn(multiB);
fn(multiC);
}
Output:
123123123
I am using a C API that defines some functions for different types. Something like:
// defined in a header:
extern "C" A* A_create();
extern "C" B* B_create();
Is it possible to call these functions from a templated C++ function such that the template type parameter determines the C function to call?
Something like:
// Template specialization for when T_create() exists
template <typename T>
auto create() -> declval( T##_create() ) {
// This is not valid syntax, but I'd like to call A_create() if this
// template is instantiated with A, B_create() if this is
// instantiated with B, etc. If the function doesn't exist, I'd like
// the compiler to fall back to the generic implementation below.
return T##_create()
}
// Template specialization for when T_create() doesn't exist
template <typename T>
T* create() {
return new T; // generic version just calls new.
}
Just wondering if its possible to do this without making explicit specializations of the create() function for each type coming from the C api.
Since there's no parameter to use, I would probably just use a function with specializations:
// generic version just calls new.
template<class T>
T* create() { return new T;}
template<>
A* create() {return A_create();}
template<>
B* create() {return B_create();}
Then usage should be trivial:
A* ptr = create<A>();
You can't avoid the specializations, but you can make them easy.
#define make_lib_create(T) template<> \
T* create() {return T##_create();}
make_lib_create(A);
make_lib_create(B);
You can create a template the accepts a callable as a parameter. The following example provides 2 examples of how the create function could be implemented.
#include <iostream>
#include <memory>
struct A
{
A() { std::cout << "A()\n"; }
~A() { std::cout << "~A()\n"; }
};
struct B
{
B() { std::cout << "B()\n"; }
~B() { std::cout << "~B()\n"; }
};
std::unique_ptr<A> create_A()
{
return std::make_unique<A>();
}
std::unique_ptr<B> create_B()
{
return std::make_unique<B>();
}
template<typename C>
auto create(C createFn)
{
return createFn();
}
template<typename T>
auto create2(T (*createFn)())
{
return createFn();
}
int main()
{
std::cout << "create()\n";
{
auto a = create(&create_A);
auto b = create(&create_B);
}
std::cout << "create2()\n";
{
auto a = create2(&create_A);
auto b = create2(&create_B);
}
return 0;
}
Output:
create()
A()
B()
~B()
~A()
create2()
A()
B()
~B()
~A()
I have implemented a simple program in C++17, in which I try to call the base class operator= from a derived class using parameter expansion. But the program does not compile.
#include "pch.h"
#include <iostream>
class D
{
public:
D()
{
std::cout << "D ctror" << std::endl;
}
D & operator = (const D & other)
{
return *this;
}
};
class C
{
public:
C()
{
std::cout << "C ctror" << std::endl;
}
C & operator = (const C & other)
{
return *this;
}
};
class B
{
public:
B()
{
std::cout << "B ctror" << std::endl;
}
B & operator = (const B & other)
{
std::cout << "operator B" << std::endl;
return *this;
}
};
template<typename... Ts> class A: public Ts...
{
public:
A(): Ts()...
{
}
A & operator = (const A & other)
{
Ts::operator =(other);
return *this;
}
};
int main()
{
A<B,C,D> a1;
A<B,C,D> a2;
a1 = a2;
}
The toolset that is used is the Visual Studio 2017 (v141)
The error that is generated is the following
error C3520: '=': parameter pack must be expanded in this context
note: while compiling class template member function 'A
&A::operator =(const A &)' note: see reference to
function template instantiation 'A &A::operator =(const
A &)' being compiled note: see reference to class template
instantiation 'A' being compiled
You need to expand the parameter pack. How about a nice fold expression:
(Ts::operator=(other), ...);
This will expand Ts... and effectively create multiple calls to operator=, one for each type in the pack.
I want to create typesafe structures that are basically identical but have different types so that they require different function signatures.
struct A {
Time t;
void doStuff(const A&);
A getStuff();
};
struct B {
Time t;
void doStuff(const B&);
B getStuff();
};
If I sue a template for the class
template<class T>
struct X {
Time t;
void doStuff(const X&);
X getStuff();
};
how can I make functions typesafe and define function signatures differently for a struct X of type A and a struct X of type B?
Try adding some unused template parameters.
template <int>
struct X{
Time t;
void doStuff(const X&); // You missed return type
X getStuff();
}; // You missed a semicolon
// Great thanks to "aschepler"
And now you can (C++11 syntax)
using A = X<1>;
using B = X<2>;
// typedef X<1> A;
// typedef X<2> B;
The following code will fail, which is what you want:
A a; B b;
a.doStuff(b); // Fail
a = b.getStuff(); // Fail
You could use a combination of Inheritance and template for that.
Following your line of thought, a possible code is:
template <class T> struct X {
int time = 10;
virtual void doStuff(const T&) = 0;
virtual T getStuff() = 0;
};
struct A : X<A>
{
void doStuff(const A& a) {
cout << "Do A stuff" << endl;
}
A getStuff() {
return *this;
}
};
struct B : X<B>
{
void doStuff(const B& value) {
cout << "Do B stuff" << endl;
}
B getStuff() {
return *this;
}
};
Then, testing we have:
int main()
{
A a; B b;
a.doStuff(a); // Ok
b.doStuff(b); // Ok
a.doStuff(b); // fail
b.doStuff(a); // fail
b = b.getStuff(); // Ok
b = a.getStuff(); // fail
return 0;
}
I would like to ask about casting in C++.
I heard that when casting is ambiguous compiler should return an error,
but, just for better understanding, I tested it and it didn't, moreover, it used functions in quite weird order. When:
A foo;
B bar = foo;
it used casting operator, but when I typed:
bar = static_cast<B>(foo);
it used single argument constructor.
Can anyone explain why it acts in this way?
The whole code which I used:
#include <iostream>
#include <typeinfo>
using namespace std;
class B;
class A {
public:
A() {}
A (const B& x);
A& operator= (const B& x);
operator B();
};
class B {
public:
B() {}
B (const A& x) {
cout << "constructor B" << endl;
}
B& operator= (const A& x) {
cout << "Assign B" << endl;
return *this;
}
operator A() {
cout << "Outer B" << endl;
return A();
}
};
A::A (const B& x) {
cout << "constructor A" << endl;
}
A& A::operator= (const B& x) {
cout << "Assign A" << endl;
return *this;
}
A::operator B() {
cout << "Outer A" << endl;
return B();
}
int main ()
{
A foo;
// First one
B bar = foo;
bar = foo;
foo = bar;
// Second one
bar = static_cast<B>(foo);
B bar2 = static_cast<B>(foo);
foo = static_cast<A>(bar);
B bar3 = foo;
A foo2 = bar3;
A foo3 = B();
foo3 = B();
return 0;
}
Edit:
My output:
Outer A
Assign B
Assign A
Copy constructor B
Copy constructor B
Copy constructor A
Outer A
Outer B
Outer B
Assign A
The reason your compiler does not complain about ambiguity is that your constructors and assignment operators take a const A/B&, but operator A() and operator B() are not declared const. For the conversion of non-const objects, the compiler therefore prefers operator A/B().
I think that the rest can be explained with the rules of static_cast conversion, which in your code amounts to behavior as in direct initialization, and overload resolution (which is why the assignment operator is only called in the last example).