>I don't follow, is DependencyInterface a proper interface (as in OOP)?

Interface is not part of OOP. C++ doesn't have it. It's still OOP. Interfaces are a language implmentation detail with a vague definition. In general an interface refers to a general concept called a type of types. An interface is a type of types.

But following your intent. Yes. It is an interface in the way you think of it.

>If so, then, the code appears more complicated compared to just implementing two classes SocketLogger and FileLogger which implement LoggerInterface (by providing log(...) method) and accept Socket and File in their constructors respectively (at least 1 method each) - 7 concepts so far (if we count the methods, too).

It's less complicated. LoggerInterface isn't a concept that exists in my example. Actually you can adjust my example for it to be more simple. Only impliment generalLogFunction. Then impliment the interface of DependencyInterface for socket and file. 3 concepts.

For oop you impliment two constructors, two log methods as well. That's 4 concepts.

>If DependencyInterface is a function pointer then it's not clear how it actually retrieves its socket without resorting to casting from void* (unsafe) or using global state (not thread-safe).

It is not a function pointer. This is isomorphic to the command pattern. Don't need to get into the specifics other then the fact that it's a awkward way to pass state.

>In any case generalLogFunction is global state. What if I want to use a different implementation for a different use case? As a client, I can't use this global function anymore => leaking abstraction. In OOP I'd be happy to work with whichever logger was injected into my instance.

A function definition is global state? A class is global state too. No difference dude. The only thing injected here is a dependency that is agnostic to logging. The socket itself is an interface to IO, but we use it here to differentiate between files and network, so can't talk about it in those terms but Socket is indeed an interface in itself.

>What will happen if the current global function is SocketLogger but I pass a file as a DependencyInterface?

generalLogFunction should handle it as a DependencyInterface. You need one implimentation of it, and you don't need logSocket or logFile. Sorry my example up there wasn't fully correct. The difference between how a file and a socket should be handled should not exist in the log function but in the implimentations of the DependencyInterface.

>What if the log function needs a second dependency? You have to add another argument to doStuff and anywhere where the log function is used.

This changes the signature of the interface.

>I see no benefits here, it's more complicated and more code.

If you don't understand my description then at least my example above sould display significantly HIGER compositional then OOP. I can construct more different types of entities from primitives with my approach then OOP.

>You lose static typing with this, because key/values stored in a context have to be cast from interface{} to the target type at runtime and there are no compile-time guarantees the value is present or of appropriate type. Usually contexts are used for cancellation.

It's not a key value store. It's indeed used for cancellation and the interface is created for such a purpose.

>It doesn't break the interface because interfaces don't have constructors. We do have to change the signature of the concrete class' constructor when dependencies change but method calls are far more common than constructor calls. When we change code due to new requirements we have to change something by definition, what we do is trying to minimize the amount of code which has to be modified. A method call is a more common case than an object construction.

Then do dependency injection on the method. The constructor is cheatcode that will cause a type error somewhere else. The dependency interface gets rid of this problem EVEN for the OOP version.