-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
std.os reorganization; new usingnamespace
semantics
#9618
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
In my code I use 'usingnamespace' for COM interfaces to minimize the amount of code duplication (see example below). Each interface defines 'Methods' function which is then used in each child interface. How could I accomplish something similar without 'usingnamespace'? Also, I use this keyword to export symbols from several different files into one common namespace, for example:
(Edit) I could put each file in its own namespace. But, is it practical? Consider some names:
|
Sorry if my comment above was confusing. I'm concerned about removing 'usingnamespace' keyword from the language. |
Thanks @michal-z - this is exactly the kind of examples I was looking for. I don't intend to yank the rug out from anyone, this is just an exploration. Once all unnecessary uses of |
So far 116 uses of I have mixed feelings about this branch, but I do think it's at least worth getting it to a mergeable state and then evaluating it. |
With the latest commit:
|
Looking around the standard library, I found two (valid) use cases: conditional exports (eg.
Example syntax: pub visible_if(T == u8) const Writer = std.io.Writer(*Self, error{OutOfMemory}, appendWrite);
pub visible_if(T == u8) fn writer(self: *Self) Writer {
return .{ .context = self };
} pub fn IndexedSet(comptime I: type, comptime Ext: fn (type) type) type {
return struct mixin (pub Ext(Self)) {
const Self = @This();
...
};
} |
@zigazeljko wouldn't I think the "conditional export" problem would also need a solution. |
@marler8997 I didn't really specify what I chose the |
I think those semantics are an excellent idea if we choose to keep something usingnamespace-like such as your proposed |
@zigazeljko would using With pub const Writer = if (T != u8)
@compileError("The Writer interface is only defined for ArrayList(u8) " ++
"but the given type is ArrayList(" ++ @typeName(T) ++ ")")
else
std.io.Writer(*Self, error{OutOfMemory}, appendWrite);
pub const writer = if (T != u8)
@compileError("The Writer interface is only defined for ArrayList(u8) " ++
"but the given type is ArrayList(" ++ @typeName(T) ++ ")")
else
(struct {
pub fn writer(self: *Self) Writer {
return .{ .context = self };
}
}).writer;
// #1717
// pub const writer = if (T != u8)
// @compileError("The Writer interface is only defined for ArrayList(u8) " ++
// "but the given type is ArrayList(" ++ @typeName(T) ++ ")")
// else
// fn(self: *Self) Writer {
// return .{ .context = self };
// }; I guess this depends on #513 to work with comptime. |
@zigazeljko @ifreund What if the user really want to put multiple symbols from different places into one common namespace? Would
|
hmmm. everywhere that mixins are used in the standard library, I find the code difficult to read. And when I try to understand why the mixin is used, I come to the conclusion that it would have been better to implement without using mixins. I'm looking specifically at std/x/os/socket.zig right now. I don't understand enums.zig enough to critique it, but maybe that itself is my critique. It's not doing something fundamentally complicated; the code should be simple to read. @michal-z's use case looks legitimate though. |
With the latest commit:
|
@andrewrk Regarding my use case above - maybe we could alter
Or
The first argument is a destination namespace and the second one is a namespace that we want to put into dest. Does this make sense? What do you think? Not sure if this solves original problem with current definition of |
@michal-z by the way, zigwin32 uses a very similar method for its COM API that utilizes pub const ID3D12Object = extern struct {
pub const VTable = extern struct {
base: IUnknown.VTable,
GetPrivateData: fn(
self: *const ID3D12Object,
guid: ?*const Guid,
pDataSize: ?*u32,
// TODO: what to do with BytesParamIndex 1?
pData: ?*c_void,
) callconv(@import("std").os.windows.WINAPI) HRESULT,
SetPrivateData: fn(
self: *const ID3D12Object,
guid: ?*const Guid,
DataSize: u32,
// TODO: what to do with BytesParamIndex 1?
pData: ?*const c_void,
) callconv(@import("std").os.windows.WINAPI) HRESULT,
SetPrivateDataInterface: fn(
self: *const ID3D12Object,
guid: ?*const Guid,
pData: ?*IUnknown,
) callconv(@import("std").os.windows.WINAPI) HRESULT,
SetName: fn(
self: *const ID3D12Object,
Name: ?[*:0]const u16,
) callconv(@import("std").os.windows.WINAPI) HRESULT,
};
vtable: *const VTable,
pub fn MethodMixin(comptime T: type) type { return struct {
pub usingnamespace IUnknown.MethodMixin(T);
// NOTE: method is namespaced with interface name to avoid conflicts for now
pub fn ID3D12Object_GetPrivateData(self: *const T, guid: ?*const Guid, pDataSize: ?*u32, pData: ?*c_void) callconv(.Inline) HRESULT {
return @ptrCast(*const ID3D12Object.VTable, self.vtable).GetPrivateData(@ptrCast(*const ID3D12Object, self), guid, pDataSize, pData);
}
// NOTE: method is namespaced with interface name to avoid conflicts for now
pub fn ID3D12Object_SetPrivateData(self: *const T, guid: ?*const Guid, DataSize: u32, pData: ?*const c_void) callconv(.Inline) HRESULT {
return @ptrCast(*const ID3D12Object.VTable, self.vtable).SetPrivateData(@ptrCast(*const ID3D12Object, self), guid, DataSize, pData);
}
// NOTE: method is namespaced with interface name to avoid conflicts for now
pub fn ID3D12Object_SetPrivateDataInterface(self: *const T, guid: ?*const Guid, pData: ?*IUnknown) callconv(.Inline) HRESULT {
return @ptrCast(*const ID3D12Object.VTable, self.vtable).SetPrivateDataInterface(@ptrCast(*const ID3D12Object, self), guid, pData);
}
// NOTE: method is namespaced with interface name to avoid conflicts for now
pub fn ID3D12Object_SetName(self: *const T, Name: ?[*:0]const u16) callconv(.Inline) HRESULT {
return @ptrCast(*const ID3D12Object.VTable, self.vtable).SetName(@ptrCast(*const ID3D12Object, self), Name);
}
};}
pub usingnamespace MethodMixin(@This());
}; |
@marler8997 Nice! I did d3d12, d2d1, dwrite, dxgi, WASAPI and others by hand.. Should switch to zigwin32 at some point.. |
I use 1 - IMHO |
I have a concrete proposal to adjust comment extracted to #9629 |
@michal-z sure I'd be happy to have another user of zigwin32 if you don't want to maintain bindings by hand as Zig evolves. I did a Zig showtime on the project if you're interested as well. |
usingnamespace
usingnamespace
semantics
This branch has implications for translate-c:
Now even for unreferenced decls such errors will be emitted. cc @Vexu @ehaas would it be feasible to define all these things? At the very least it could put |
I just created #9664 which should take care of simple macros. Now that I think of it, we'll also need to check if names exist in the c_builtins namespace. Function-like macros will need additional work to check if identifiers exist in the scope or as an argument or as a c_builtin. We'll also need to update how the c_builtins package gets used - it currently imports with |
@andrewrk @Vexu how would you feel about inlining the contents of |
Also move some usingnamespace test cases from compare_output to behavior.
This commit reapplies 4f0aa7d.
This fixes a merge conflict when rebasing against master branch.
I incorrectly assumed that __kernel_timespec was used when not linking libc, however that is not the case. `std.os.timespec` is used both for libc and non-libc cases. `__kernel_timespec` is a special struct that is used only for io_uring.
9c2e401
to
2264fca
Compare
Originally, the main purpose of this branch was to explore avoiding the
usingnamespace
feature of the zig language, specifically with regardsto
std.os
and related functionality.Ultimately the decision resulted in the accepted proposal #9629. This branch now implements that proposal via a new compile error in AstGen.zig: "use of undeclared identifier". This requires sweeping changes to the standard library, so I took the opportunity to do a reorganization.
This is progress towards closing #6600 since it clarifies where the
actual "owner" of each declaration is, and reduces the number of
different ways to import the same declarations.
One of the main organizational strategies used here is to do namespacing
with real namespaces (e.g. structs) rather than by having declarations
share a common prefix (the C strategy). It's no coincidence that
usingnamespace
has similar semantics to#include
and becomes muchless necessary when using proper namespaces.