mirror of
				https://github.com/bspeice/speice.io
				synced 2025-11-03 10:00:37 -05:00 
			
		
		
		
	Further refining remote types
This commit is contained in:
		@ -178,19 +178,19 @@ int main() {
 | 
				
			|||||||
      |                   ^~~~~~
 | 
					      |                   ^~~~~~
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Local trait implementation of remote data type?
 | 
					# Implement methods on remote types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AKA "extension methods". UFCS can accomplish this, and could use free functions to handle instead,
 | 
					Rust allows both arbitrary `self` and extension traits. Arbitrary self forms the basis of the
 | 
				
			||||||
but having the IDE auto-complete `.<the next thing>` is exceedingly useful, as opposed to memorizing
 | 
					`async` system in Rust. Extension traits form basis of `futures` library. Accomplish effectively the
 | 
				
			||||||
what functions are necessary for conversion. We're not changing what's possible, just making it
 | 
					same thing, but for concrete types and traits respectively.
 | 
				
			||||||
easier for humans.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Likely requires sub-classing the remote class. Implicit conversions don't _really_ work because they
 | 
					UFCS would achieve the same effect, but unclear if/when it will be available:
 | 
				
			||||||
must be defined on the remote type (not true: `operator Local` must be defined on remote, but
 | 
					https://dancrn.com/2020/08/02/ufcs-in-clang.html
 | 
				
			||||||
`Local` could have a `Local(const Remote&)` implicit constructor). Could maybe use wrapper classes
 | 
					
 | 
				
			||||||
that have single-arg (implicit) constructors, and get away with it as long as the wrapper knows it's
 | 
					Can use free functions in the meantime, but having the IDE auto-complete `.<the next thing>` is
 | 
				
			||||||
not safe to modify the internals. That said, wrapper can only use the public interface unless
 | 
					exceedingly useful, as opposed to looking through all functions in a namespace.
 | 
				
			||||||
declared friend (which is no different to Rust).
 | 
					
 | 
				
			||||||
 | 
					Can also sub-class or implicitly convert to a wrapper:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```c++
 | 
					```c++
 | 
				
			||||||
#include <concepts>
 | 
					#include <concepts>
 | 
				
			||||||
@ -227,27 +227,51 @@ void regular_func(LocalImpl value) {
 | 
				
			|||||||
int main() {
 | 
					int main() {
 | 
				
			||||||
    SomeRemoteClass x {};
 | 
					    SomeRemoteClass x {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // This isn't OK because `auto` doesn't automatically convert to `LocalImpl`
 | 
					    // This _will not_ compile because `auto` doesn't trigger the conversion to `LocalImpl`
 | 
				
			||||||
    //auto_func(x);
 | 
					    //auto_func(x);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // This _is_ OK because we explicitly declare the class we want (`LocalImpl`) and `SomeRemoteClass`
 | 
					    // This _will_ compile because the function signature declares a concrete class for which an
 | 
				
			||||||
    // is implicitly converted. Just so happens that `LocalImpl` implements `MyConcept`.
 | 
					    // implicit conversion is available. It just so happens that `LocalImpl` satisfies `MyConcept`.
 | 
				
			||||||
    regular_func(x);
 | 
					    regular_func(x);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // We could extend the conversion pattern using specializations of `LocalImpl`, or maybe use
 | 
					 | 
				
			||||||
    // `std::variant` to hold different internal types, but there's still a disconnect between
 | 
					 | 
				
			||||||
    // what we actually want to fulfill (`MyConcept`) and how that's implemented for remote types
 | 
					 | 
				
			||||||
    // (using the `LocalImpl` wrapper and implicit conversions).
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Rust makes this weird because you have to `use ClientExt` to bring the methods in scope, but the
 | 
					The `LocalImpl` wrapper could be extended to handle additional remote types using template
 | 
				
			||||||
trait name might not show up because `impl ClientExt for RemoteStruct` is defined elsewhere.
 | 
					specialization or holding an internal `std::variant`, but that misses the point: we want to write
 | 
				
			||||||
Alternately, `ClientExt: AnotherTrait` implementations where the default `ClientExt` implementation
 | 
					code that accepts anything that satisfies `MyConcept`. When we write functions that require a
 | 
				
			||||||
is used. To do this, Rust compiles the entire crate as a single translation unit, and the orphan
 | 
					specific wrapper, we're being overly restrictive, and obfuscating our intentions (we don't actually
 | 
				
			||||||
rule.
 | 
					care about the wrapper, it's just there for ease-of-use).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Rust can do one thing special though - can run methods on literals - `42.my_method()`.
 | 
					Can use some overloading/specialization tricks for ease of use:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```c++
 | 
				
			||||||
 | 
					auto some_func_(MyConcept auto value) -> void {
 | 
				
			||||||
 | 
					    auto x = value.do_something();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto some_func(MyConcept auto value) -> void {
 | 
				
			||||||
 | 
					    some_func_(value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void some_func(LocalImpl value) {
 | 
				
			||||||
 | 
					    some_func_(value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Need to be careful though:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```c++
 | 
				
			||||||
 | 
					auto some_func(MyConcept auto value) -> void {
 | 
				
			||||||
 | 
					    auto x = value.do_something();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void some_func(LocalImpl value) {
 | 
				
			||||||
 | 
					    // NOTE: This is actually a recursive call because `LocalImpl` is more specific than `auto`,
 | 
				
			||||||
 | 
					    // so will overflow the stack.
 | 
				
			||||||
 | 
					    // We use `some_func_` above to uniquely name the function we actually want to call.
 | 
				
			||||||
 | 
					    some_func(value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Checking a type fulfills the concept
 | 
					# Checking a type fulfills the concept
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user