Skip to content
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

Draft: More STL container types #7

Open
kassane opened this issue Jul 2, 2023 · 19 comments
Open

Draft: More STL container types #7

kassane opened this issue Jul 2, 2023 · 19 comments

Comments

@kassane
Copy link
Contributor

kassane commented Jul 2, 2023

zig version: 0.11.0-dev.3905+309aacfc8

std::string

Test

Update: Wrong!! No C++ receiver in DoSomething().

$> zig build test
run test: error: SampleObject is doing something
// in tests.zig
test "cpp_string" {
    const ffi = @import("c020_string_shared.zig");

    const cstr: [*:0]const u8 = "Hello World";
    const str = cpp.String().init(.{}, cstr);
    try std.testing.expectEqual(str.str, cstr);
    // const shared = cpp.SharedPtr(ffi.SampleObject);
    // try expect(shared.use_count() == @atomicLoad(usize, null, .{.Relaxed}));
    var object = ffi.SampleObject.init();
    // failed: expected u8@0, found u8@208ecc
    // try std.testing.expectEqual(object.msg.get(), "");
    object.DoSomething();
    std.debug.print("{s}\n", .{object.msg.str});
    try std.testing.expectEqual(object.msg.get(), "SampleObject is doing something\n");
}

source:

// auto generated by c2z
const std = @import("std");
const cpp = @import("cpp");

pub const SampleObject = extern struct {
    msg: cpp.String(),

    extern fn _ZN12SampleObjectC1Ev(self: *SampleObject) void;
    pub inline fn init() SampleObject {
        var self: SampleObject = undefined;
        _ZN12SampleObjectC1Ev(&self);
        return self;
    }

    extern fn _ZN12SampleObjectD1Ev(self: *SampleObject) void;
    pub inline fn deinit(self: *SampleObject) void {
        self._ZN12SampleObjectD1Ev();
    }

    pub fn DoSomething(self: *SampleObject) void {
        const tmp: [*:0]const u8 = "SampleObject is doing something\n";
        self.msg.str = tmp;
    }
};

pub const SampleLibrary = extern struct {
    pub fn CreateObject(self: *SampleLibrary) cpp.SharedPtr(SampleObject) {
        _ = self;
        return;
    }
};
#pragma once

#include <iostream>
#include <memory>
#include <string>

class SampleObject {
    std::string msg;
public:
    SampleObject() {
        std::cout << "SampleObject created\n";
    }

    ~SampleObject() {
        std::cout << "SampleObject destroyed\n";
    }

    void DoSomething() {
        msg = "SampleObject is doing something\n";
    }
};

class SampleLibrary {
public:
    static std::shared_ptr<SampleObject> CreateObject() {
        return std::make_shared<SampleObject>();
    }
};

Impl - WiP

// UniquePtr implementation
pub fn UniquePtr(comptime T: type) type {
    return extern struct {
        const Self = @This();

        ptr: ?*T = null,

        pub fn init(ptr: *T) Self {
            return .{ .ptr = ptr };
        }

        pub fn get(self: *const Self) *T {
            return self.ptr.?;
        }

        pub fn release(self: *Self) *T {
            const ptr = self.ptr.?;
            self.ptr = null;
            return ptr;
        }

        pub fn reset(self: *Self, ptr: *T) void {
            if (self.ptr) {
                std.c.free(self.ptr);
            }
            self.ptr = ptr;
        }

        pub fn deinit(self: *Self) void {
            if (self.ptr) {
                std.c.free(self.ptr);
            }
        }
    };
}

// SharedPtr implementation
pub fn SharedPtr(comptime T: type) type {
    const AtomicUsize = std.atomic.Atomic(usize);
    return extern struct {
        const Self = @This();

        ptr: ?*T = null,
        counter: *AtomicUsize = null,

        pub fn init(ptr: *T) Self {
            const counter = std.heap.malloc(std.sizeOf(AtomicUsize));
            AtomicUsize.init(1);
            return .{ .ptr = ptr, .counter = counter };
        }

        pub fn get(self: *const Self) *T {
            return self.ptr.?;
        }

        pub fn use_count(self: *const Self) usize {
            return AtomicUsize.load(self.counter, .{.Relaxed});
        }

        pub fn acquire(self: *Self) void {
            AtomicUsize.fetchAdd(self.counter, 1, .{.Acquire});
        }

        pub fn release(self: *Self) void {
            if (AtomicUsize.fetchSub(self.counter, 1, .{.Release}) == 1) {
                AtomicUsize.store(self.counter, 0, .{.Release});
                AtomicUsize.release(self.counter);
                std.c.free(self.counter);
                std.c.free(self.ptr);
            }
        }

        pub fn deinit(self: *Self) void {
            AtomicUsize.release(self.counter);
            std.c.free(self.counter);
            std.c.free(self.ptr);
        }
    };
}

// String with allocator
pub fn StringAlloc(
    comptime Alloc: type,
) type {
    return extern struct {
        const Self = @This();

        str: [*:0]const u8,
        allocator: Alloc,

        pub fn init(allocator: Alloc, value: [*:0]const u8) Self {
            return .{ .str = value, .allocator = allocator };
        }

        pub fn get(self: *const Self) [*:0]const u8 {
            return self.str;
        }

        pub fn deinit(self: *Self) void {
            if (std.mem.len(self.str) != 0) {
                std.c.free(@as(?*anyopaque, @constCast(self.str)));
            }
        }
    };
}

// String with default allocator
pub fn String() type {
    return StringAlloc(Allocator(u8));
}
@kassane kassane changed the title Draft: More types Draft: More STL container types Jul 2, 2023
@kassane
Copy link
Contributor Author

kassane commented Jul 2, 2023

The types of atomic sorting need to be completely revised.
I was only able to test the string.

@kassane
Copy link
Contributor Author

kassane commented Jul 2, 2023

By the way, during the tests I saw (std.debug.print) that in the for-loop of the cpp_vector test, empty values ​​with zero elements are compared.

@lassade
Copy link
Owner

lassade commented Jul 2, 2023

I don't think the String impl is right, did it pass the tests ?!?! If you look at how cpp.Vector you see that C++ stores 3 pointers one for the start other for the end of the used data and other pointing to the end of the memory slice;

Try to remove the implementations of SampleObject from the header file, so the transpiled code acctually calls c++ compiled code, the transpiler it's far from been able to handle all c++ semantics, specially when dealing with std containers.

@kassane
Copy link
Contributor Author

kassane commented Jul 2, 2023

I don't think the String impl is right, did it pass the tests ?!?!

Yeah!

However, as I mentioned, the update would be from Zig to Zig. I can't receive C++ strings just like cpp_vector doesn't receive values.


As for smart pointers, I'll need to do implementation tests. Although it compiles without error.

@kassane
Copy link
Contributor Author

kassane commented Jul 2, 2023

the transpiler it's far from been able to handle all c++ semantics, specially when dealing with std containers.

I agree! I'll try use the .c_str() in std::string and .data() in std::vector and std::array when carrying out tests on the containers.

@lassade
Copy link
Owner

lassade commented Jul 4, 2023

I did some work implement std::string, it's working but only for gnu abi, is in the newest branch.

@kassane
Copy link
Contributor Author

kassane commented Jul 4, 2023

I did some work implement std::string, it's working but only for gnu abi, is in the newest branch.

I've seen and tested it and also it's not available for msvc yet.

A question: Did you try viewing std::string from llvm-libcxx?

@lassade
Copy link
Owner

lassade commented Jul 4, 2023

llvm-libcxx ? why the particular interest on msvc? is there any lib or project that requires it?

@kassane
Copy link
Contributor Author

kassane commented Jul 4, 2023

llvm-libcxx ? why the particular interest on msvc? is there any lib or project that requires it?

Two aspects here.

First, we already know that msvc is microsoft's standard abi. To date zig toolchain (clang-cl) does not support libc++ in msvc target, making nostdlib++.

Second, about libc++ and libstdc++ know that they are not that compatible as expected. Having zig toolchain as primary objective it's usual to think llvm-libcxx only except you want to host-build (killing cross-compilation).
https://github.com/ziglang/zig/blob/master/lib/libcxx/src/string.cpp

e.g. (zig c++ w/ libstdc++): https://gist.github.com/kassane/7e9a2da137e13eb6e1dbab726693bdb7?permalink_comment_id=4475986#gistcomment-4475986

Reference: https://github.com/ziglang/zig/wiki/Troubleshooting-Build-Issues#dual-abi-linking


Most game devlopers look for msvc compatibility, being the case of the zig-gamedev project and unreal (TODO: testing...)!

@lassade
Copy link
Owner

lassade commented Jul 4, 2023

I don't understand what you mean with the first part

@kassane
Copy link
Contributor Author

kassane commented Jul 4, 2023

I don't understand what you mean with the first part

What exactly?

MSVC target doesn't use zig libcxx and requires linking windows crt and sdk libraries directly.

--- edit

Missing Second,

- About libc++ and libstdc++ know
+ Second, about libc++ and libstdc++ know

@lassade
Copy link
Owner

lassade commented Jul 4, 2023

MSVC string works completly different from the clang implementation :( it requires a second implementation

For some reasong I can't build with:

zig build test -Doptimize=ReleaseFast -freference-trace -Dtarget=native-windows-msvc

@kassane
Copy link
Contributor Author

kassane commented Jul 4, 2023

For some reasong I can't build with:

zig build test -Doptimize=ReleaseFast -freference-trace -Dtarget=native-windows-msvc

Could you show the error that occurred in the msvc build?

My case (Linux user), I use xwin to download libraries + includes sdk and crt (VS 2019/ 16 version).
https://github.com/kassane/c2z/blob/msvc/build.zig#L100

The last CI commit returns the @compileError to msvc.

@lassade
Copy link
Owner

lassade commented Jul 4, 2023

Ok I manage to build, but the tests exit with error code 5, have no clue how to fix it.

@lassade
Copy link
Owner

lassade commented Jul 7, 2023

I finnaly figure out, it was a small mistake on my end, I had to make MSVC work in debug builds and it does sort of does ...

By the way xWin isn't needed at all

@kassane
Copy link
Contributor Author

kassane commented Jul 7, 2023

I finnaly figure out, it was a small mistake on my end, I had to make MSVC work in debug builds and it does sort of does

Great!

By the way xWin isn't needed at all

Yes! needed for non-windows or another arch-msvc only. Because zig toolchain statically configures msvc target (it does not switch to x86 or aarch64).

@kassane
Copy link
Contributor Author

kassane commented Jul 7, 2023

@lassade, have you tested astd::vector<std::vector<T>>?

@lassade
Copy link
Owner

lassade commented Jul 7, 2023

no, but theres no reason why it can't work

@kassane
Copy link
Contributor Author

kassane commented Jul 7, 2023

no, but theres no reason why it can't work

Transpiling test

- bool enumerate(std::vector<uint8_t>& out_buf, size_t count);
+ bool enumerate(std::vector<std::vector<uint8_t>>& out_buf, size_t count);
info: using host `x86_64-linux-gnu` as target
info: zig cc -x c++ -lc++ -Xclang -ast-dump=json -fsyntax-only -target x86_64-linux-gnu 
info: binding `test_cases/include/c013_cpp_vector.h`
thread 51809 panic: integer overflow
/home/kassane/Documentos/c2z/src/Transpiler.zig:2436:34: 0x2845be in transpileType (c2z)
    const ch = ttname[ttname.len - 1];
                                 ^
/home/kassane/Documentos/c2z/src/Transpiler.zig:2553:48: 0x287a4a in transpileArgs (c2z)
            const name = try self.transpileType(arg);
                                               ^
/home/kassane/Documentos/c2z/src/Transpiler.zig:2548:35: 0x2878d4 in transpileArgs (c2z)
            try self.transpileArgs(args, buffer, index);
                                  ^
/home/kassane/Documentos/c2z/src/Transpiler.zig:2501:31: 0x285cee in transpileType (c2z)
        try self.transpileArgs(ttname[less_than..], &args, &index);
                              ^
/home/kassane/Documentos/c2z/src/Transpiler.zig:2464:43: 0x284d78 in transpileType (c2z)
        var inner = try self.transpileType(raw_name);
                                          ^
/home/kassane/Documentos/c2z/src/Transpiler.zig:1060:52: 0x29252a in visitCXXMethodDecl (c2z)
                var z_type = try self.transpileType(c_type);
                                                   ^
/home/kassane/Documentos/c2z/src/Transpiler.zig:1548:35: 0x259a9a in visit (c2z)
    return self.visitCXXMethodDecl(value, null);
                                  ^
/home/kassane/Documentos/c2z/src/Transpiler.zig:425:27: 0x282158 in visitTranslationUnitDecl (c2z)
            try self.visit(item);
                          ^
/home/kassane/Documentos/c2z/src/Transpiler.zig:312:42: 0x259773 in visit (c2z)
        try self.visitTranslationUnitDecl(value);
                                         ^
/home/kassane/Documentos/c2z/src/Transpiler.zig:191:19: 0x24816c in run (c2z)
    try self.visit(value);
                  ^
/home/kassane/Documentos/c2z/src/main.zig:130:27: 0x244d84 in main (c2z)
        try transpiler.run(&tree.value);
                          ^
/home/kassane/zig/0.11.0-dev.3937+78eb3c561/files/lib/std/start.zig:608:37: 0x23eea4 in posixCallMainAndExit (c2z)
            const result = root.main() catch |err| {
                                    ^
/home/kassane/zig/0.11.0-dev.3937+78eb3c561/files/lib/std/start.zig:367:5: 0x23e991 in _start (c2z)
    @call(.never_inline, posixCallMainAndExit, .{});
    ^
[1]    51809 IOT instruction (core dumped)  ./zig-out/bin/c2z -no-glue test_cases/include/c013_cpp_vector.h
``

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants