• Create Account

### #Actualdmatter

Posted 12 September 2012 - 04:09 AM

The issue is that you are violating the Liskov Substitution Principle (LSP).
So your interface has set up this contract stating that IWindow::addControl takes an IControl. In reality this is a lie because concrete subclasses such as FooWindow in fact require a FooControl, not an IControl.

The LSP only permits contravariance of function parameter types in subclasses (which means making the type more general), whereas what you're achieving through the use of casting is covariance (making the type more specific). Failure to abide by the LSP will mean that (without casting and therefore also violating the Open-Closed Principle) you cannot safely use the objects polymorphically.

One solution is to conclude that addControl is not something you can abstract away, since a FooWindow needs FooControls and a QtWindow needs QtControls, so you simply have a non-virtual addControl(FooControl &) function in a FooWindow. If you want to do the GUI building in an API-agnostic fashion then use static duck-type polymorphism:
template <class FactoryT>
void buildGui(FactoryT guiFactory)
{
FactoryT::WindowType window = guiFactory.createWindow();
FactoryT::ControlType ctrlA = guiFactory.createControl();
FactoryT::ControlType ctrlB = guiFactory.createControl();

}


It turns out we actually can abstract addControl, we just have to realise that making it consume a Control as a parameter is futile. It will instead need to be a producer of controls, this works because the LSP does permit covariance of return types! So we can hide the implementation behind the return type.
class FooWindow : public IWindow
{
FooFramework::ApplicationWindow * theActualWindow;

IButton * addButton() {
FooButton * button = new FooButton();
return button;
}
}

// elsewhere...
IWindow * window = new FooWindow();
IButton * button = window->addButton();


Another way to go is to just select the implementation at build-time. So you have a non-abstract Window class declaration in a header and several different source/cpp files that implement Window but for different frameworks (Foo, Qt, etc). Which cpp file you use is selected by the pre-processer using macros (or some other kind of variant on this idea.. linking a particular static library, etc). In order for Window to get access to the underlying framework poiner from a Control object the Control class can have a getUnderylingPtr() function, this could return a void pointer (and accept a static_cast, which we know will be safe, and fast, because the implementation was chosen statically), or abstract the type with a typedef that the implementation chooses, or use the curiously-recurring template pattern (CRTP) to avoid losing type information.

### #7dmatter

Posted 12 September 2012 - 04:05 AM

The issue is that you are violating the Liskov Substitution Principle (LSP).
So your interface has set up this contract stating that IWindow::addControl takes an IControl. In reality this is a lie because concrete subclasses such as FooWindow in fact require a FooControl, not an IControl.

The LSP only permits contravariance of function parameter types in subclasses (which means making the type more general), whereas what you're achieving through the use of casting is covariance (making the type more specific). Failure to abide by the LSP will mean that (without casting and therefore also violating the Open-Closed Principle) you cannot safely use the objects polymorphically.

One solution is to conclude that addControl is not something you can abstract away, since a FooWindow needs FooControls and a QtWindow needs QtControls, so you simply have a non-virtual addControl(FooControl &) function in a FooWindow. If you want to do the GUI building in an API-agnostic fashion then use static duck-type polymorphism:
template <class FactoryT>
void buildGui(FactoryT guiFactory)
{
FactoryT::WindowType window = guiFactory.createWindow();
FactoryT::ControlType ctrlA = guiFactory.createControl();
FactoryT::ControlType ctrlB = guiFactory.createControl();

}


It turns out we actually can abstract addControl, we just have to realise that making it consume a Control as a parameter is futile. It will instead need to be a producer of controls, this works because the LSP does permit covariance of return types! So we can hide the implementation behind the return type.
class FooWindow : public IWindow
{
FooFramework::ApplicationWindow * theActualWindow;

IButton * addButton() {
FooButton * button = new FooButton();
return button;
}
}

// elsewhere...
IWindow * window = new FooWindow();
IButton * button = window->addButton();


Another way to go is to just select the implementation at build-time. So you have a non-abstract Window class declaration in a header and several different source/cpp files that implement Window but for different frameworks (Foo, Qt, etc). Which cpp file you use is selected by the pre-processer using macros (or some other kind of variant on this idea.. linking a particular static library, etc). In order for Window to get access to the underlying framework poiner from a Control object the Control class can have a getUnderylingPtr() function, this could return a void pointer (and accept a static_cast, which we know will be safe, and fast, because the implementation was chosen statically) or you can use the curiously-recurring template pattern (CRTP) to avoid losing type information and therefore avoid the cast too.

### #6dmatter

Posted 11 September 2012 - 06:17 PM

The issue is that you are violating the Liskov Substitution Principle (LSP).
So your interface has set up this contract stating that IWindow::addControl takes an IControl. In reality this is a lie because concrete subclasses such as FooWindow in fact require a FooControl, not an IControl.

The LSP only permits contravariance of function parameter types in subclasses (which means making the type more general), whereas what you're achieving through the use of casting is covariance (making the type more specific). Failure to abide by the LSP will mean that (without casting and therefore also violating the Open-Closed Principle) you cannot safely use the objects polymorphically.

One solution is conclude that addControl is not something you can abstract away, since a FooWindow needs FooControls and a QtWindow needs QtControls, so you simply have a non-virtual addControl(FooControl &) function in a FooWindow. If you want to do the GUI building in an API-agnostic fashion then use static duck-type polymorphism:
template <class FactoryT>
void buildGui(FactoryT guiFactory)
{
FactoryT::WindowType window = guiFactory.createWindow();
FactoryT::ControlType ctrlA = guiFactory.createControl();
FactoryT::ControlType ctrlB = guiFactory.createControl();

}


It turns out we actually can abstract addControl, we just have to realise that making it consume a Control as a parameter is futile. It will instead need to be a producer of controls, this works because the LSP does permit covariance of return types! So we can hide the implementation behind the return type.
class FooWindow : public IWindow
{
FooFramework::ApplicationWindow * theActualWindow;

IButton * addButton() {
FooButton * button = new FooButton();
return button;
}
}

// elsewhere...
IWindow * window = new FooWindow();
IButton * button = window->addButton();


Another way to go is to just select the implementation at build-time. So you have a non-abstract Window class declaration in a header and several different source/cpp files that implement Window but for different frameworks (Foo, Qt, etc). Which cpp file you use is selected by the pre-processer using macros (or some other kind of variant on this idea.. linking a particular static library, etc). In order for Window to get access to the underlying framework poiner from a Control object the Control class can have a getUnderylingPtr() function, this could return a void pointer (and accept a static_cast, which we know will be safe, and fast, because the implementation was chosen statically) or you can use the curiously-recurring template pattern (CRTP) to avoid losing type information and therefore avoid the cast too.

### #5dmatter

Posted 11 September 2012 - 06:14 PM

The issue is that you are violating the Liskov Substitution Principle (LSP).
So your interface has set up this contract stating that IWindow::addControl takes an IControl. In reality this is a lie because concrete subclasses such as FooWindow in fact require a FooControl, not an IControl.

The LSP only permits contravariance of function parameter types in subclasses (which means making the type more general), whereas what you're achieving through the use of casting is covariance (making the type more specific). Failure to abide by the LSP will mean that (without casting and therefore also violating the Open-Closed Principle) you cannot safely use the objects polymorphically.

One solution is conclude that addControl is not something you can abstract away, since a FooWindow needs FooControls and a QtWindow needs QtControls, so you simply have a non-virtual addControl(FooControl &) function in a FooWindow. If you want to do the GUI building in an API-agnostic fashion then use static duck-type polymorphism:
template <class FactoryT>
void buildGui(FactoryT guiFactory)
{
FactoryT::WindowType window = guiFactory.createWindow();
FactoryT::ControlType ctrlA = guiFactory.createControl();
FactoryT::ControlType ctrlB = guiFactory.createControl();

}


It turns out we actuall can abstract addControl, we just have to realise that making it consume a Control as a parameter is futile. It will instead need to be a producer of controls, this works because the LSP does permit covariance of return types! So we can hide the implementation behind the return type.
class FooWindow : public IWindow
{
FooFramework::ApplicationWindow * theActualWindow;

IButton * addButton() {
FooButton * button = new FooButton();
return button;
}
}

// elsewhere...
IWindow * window = new FooWindow();
IButton * button = window->addButton();


Another way to go is to just select the implementation at build-time. So you have a non-abstract Window class declaration in a header and several different source/cpp files that implement Window but for different frameworks (Foo, Qt, etc). Which cpp file you use is selected by the pre-processer using macros (or some other kind of variant on this idea.. linking a particular static library, etc). In order for Window to get access to the underlying framework poiner from a Control object the Control class can have a getUnderylingPtr() function, this could return a void pointer (and accept a static_cast, which we know will be safe, and fast, because the implementation was chosen statically) or you can use the curiously-recurring template pattern (CRTP) to avoid losing type information and therefore avoid the cast too.

### #4dmatter

Posted 11 September 2012 - 06:13 PM

The issue is that you are violating the Liskov Substitution Principle (LSP).
So your interface has set up this contract stating that IWindow::addControl takes an IControl. In reality this is a lie because concrete subclasses such as FooWindow in fact require a FooControl, not an IControl.

The LSP only permits contravariance of function parameter types in subclasses (which means making the type more general), whereas what you're achieving through the use of casting is covariance (making the type more specific). Failure to abide by the LSP will mean that (without casting and therefore also violating the Open-Closed Principle) you cannot safely use the objects polymorphically.

One solution is conclude that addControl is not something you can abstract away, since a FooWindow needs FooControls and a QtWindow needs QtControls, so you simply have a non-virtual addControl(FooControl &) function in a FooWindow. If you want to do the GUI building in an API-agnostic fashion then use static duck-type polymorphism:
template <class FactoryT>
void buildGui(FactoryT guiFactory)
{
FactoryT::WindowType window = guiFactory.createWindow();
FactoryT::ControlType ctrlA = guiFactory.createControl();
FactoryT::ControlType ctrlB = guiFactory.createControl();

}


It turns out we actuall can abstract addControl, we just have to realise that making it consume a Control as a parameter is futile. It will instead need to be a producer of controls, this works because the LSP does permit covariance of return types! So we can hide the implementation behind the return type.
class FooWindow : public IWindow
{
FooFramework::ApplicationWindow * theActualWindow;

IButton * addButton() {
FooButton * button = new FooButton();
return button;
}
}

// elsewhere...
IWindow * window = new FooWindow();
IButton * button = window->addButton();


Another way to go is to just select the implementation at build-time. So you have a non-abstract Window class declaration in a header and several different source/cpp files that implement Window but for different frameworks (Foo, Qt, etc). Which cpp file you use is selected by the pre-processer using macros (or some other kind of variant on this idea.. linking a particular static library, etc). In order for Window to get access to the underlying framework poiner from a Control object the Control class can have a getUnderylingPtr() function, this could return a void pointer (and accept a static_cast, which we know will be safe, and fast, because the implementation was chosen statically) or you can use the curiously-recurring template pattern (CRTP) to avoid losing type information and therefore avoid the cast too.

### #3dmatter

Posted 11 September 2012 - 06:11 PM

The issue is that you are violating the Liskov Substitution Principle (LSP).
So your interface has set up this contract stating that IWindow::addControl takes an IControl. In reality this is a lie because concrete subclasses such as FooWindow in fact require a FooControl, not an IControl.

The LSP only permits contravariance of function parameter types in subclasses (which means making the type more general), whereas what you're achieving through the use of casting is covariance (making the type more specific). Failure to abide by the LSP will mean that (without casting and therefore also violating the Open-Closed Principle) you cannot safely use the objects polymorphically.

One solution is conclude that addControl is not something you can abstract away, since a FooWindow needs FooControls and a QtWindow needs QtControls, so you simply have a non-virtual addControl(FooControl &) function in a FooWindow. If you want to do the GUI building in an API-agnostic fashion then use static duck-type polymorphism:
template <class FactoryT>
void buildGui(FactoryT guiFactory)
{
FactoryT::WindowType window = guiFactory.createWindow();
FactoryT::ControlType ctrlA = guiFactory.createControl();
FactoryT::ControlType ctrlB = guiFactory.createControl();

}


It turns out we actuall can abstract addControl, we just have to realise that making it consume a Control as a parameter is futile. It will instead need to be a producer of controls, this works because the LSP does permit covariance of return types! So we can hide the implementation behind the return type.
class FooWindow : public IWindow
{
FooFramework::ApplicationWindow * theActualWindow;
IButton * addButton() {
FooButton * button = new FooButton();