I'm working on a Path library based around C APIs.
In the open(2)
man page, the open function has either of the following signatures:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
In the opendir(3)
man page, then opendir function has the following signature:
int opendir(const char *name);
I have a protocol Openable
for types that can be opened:
public protocol Openable: Path {
var fileDescriptor: Int32 { get }
func close() throws
func open(/* what do I put here */) throws -> Open<Self>
}
I want to require some kind of open
function in the Openable protocol, but I'm not sure what I should use for the function signature. The C open directory function (same on Mac and Linux) does not require anything other than the path name, but the C open file function requires 2-3 arguments. Is there some way to make a dynamic function signature in protocols?
I did something like this (not exactly, but close enough):
public protocol Openable: Path {
associatedtype OpenOptions
var fileDescriptor: Int32 { get }
func close() throws
func open(_ options: OpenOptions) throws -> Open<Self>
}
extension FilePath: Openable {
public typealias OpenOptions = (options: Int, mode: FileMode?) // mode is only needed when creating paths
public func open(_ options: OpenOptions) throws -> Open<FilePath> {
// Implementation using `open(2)` here...
}
public func open(permissions: OpenFilePermissions, flags: OpenFileFlags = [], mode: FileMode? = nil) throws -> Open<FilePath> {
return try open((options: permissions.rawValue | flags.rawValue, mode: mode))
}
}
extension DirectoryPath: Openable {
public typealias OpenOptions = ()
public func open(_ options: () = ()) throws -> Open<DirectoryPath> {
// Implementation using `opendir(3)` here...
}
}
Which worked, but I'd really just rather have the entire signature be dynamic rather than just using a single parameter that's a unique tuple based on the PathType. I don't want to just remove the need for the open function from the protocol because then it's a hidden implementation detail that your type needs to include some way to open the path and I prefer to be explicit in my protocol declaration.
Basically, doing it this way satisfies the protocol requirement, but the protocol function will probably only ever be called indirectly on FilePath and I prefer not to have (public) functions that I know won't ever be used directly.
I also tried this:
public protocol Openable: Path {
associatedtype OpenOptions
var openFunc: (OpenOptions) throws -> Open<Self> { get }
}
extension FilePath: Openable {
public typealias OpenOptions = (permissions: OpenFilePermissions, flags: OpenFileFlags, mode: FileMode?)
public var openFunc: (OpenOptions) throws -> Open<Self> { return self.openFile }
public func open(permissions: OpenFilePermissions, flags: OpenFileFlags = [], mode: FileMode? = nil) throws -> Open<FilePath> {
// Implementation using `open(2)` here...
}
But it fails to compile since the signature of openFile doesn't match what openFunc expects. If I remove the OpenOptions' tuple labels then I get closer to the expected type.
Is there any way to have dynamic function signatures? Or possibly to pass a tuple as a functions arguments?