From 80d80ad8aa1d954d1cf81a0ef1fd20f54086bbe0 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 13 Jul 2024 17:51:06 +0800 Subject: [PATCH 1/4] library: cpp/std; cpp/inih demo: inihreader --- _demo/hello/hello.go | 2 +- c/socket/socket.go | 16 +++ cpp/inih/READEME.md | 15 ++- cpp/inih/_demo/inihdemo/ReaderDemo.go | 61 ------------ cpp/inih/_demo/inihdemo/inihdemo.go | 2 +- cpp/inih/_demo/inihreader/config.ini | 10 ++ cpp/inih/_demo/inihreader/reader_demo.go | 31 ++++++ cpp/inih/inih.go | 18 +++- cpp/inih/inihReader.go | 64 ------------ cpp/inih/reader.go | 73 ++++++++++++++ cpp/std/_cppstd/string.cpp | 43 ++++++++ cpp/std/_demo/cpphello/cppstr.go | 12 +++ cpp/std/std.go | 24 +++++ cpp/std/string.go | 121 +++++++++++++++++++++++ 14 files changed, 363 insertions(+), 129 deletions(-) delete mode 100644 cpp/inih/_demo/inihdemo/ReaderDemo.go create mode 100644 cpp/inih/_demo/inihreader/config.ini create mode 100644 cpp/inih/_demo/inihreader/reader_demo.go delete mode 100644 cpp/inih/inihReader.go create mode 100644 cpp/inih/reader.go create mode 100644 cpp/std/_cppstd/string.cpp create mode 100644 cpp/std/_demo/cpphello/cppstr.go create mode 100644 cpp/std/std.go create mode 100644 cpp/std/string.go diff --git a/_demo/hello/hello.go b/_demo/hello/hello.go index 7437c3e8..5d2cdd76 100644 --- a/_demo/hello/hello.go +++ b/_demo/hello/hello.go @@ -9,5 +9,5 @@ func main() { } /* Expected output: -Hello World +Hello world */ diff --git a/c/socket/socket.go b/c/socket/socket.go index c4e24da6..d019cf62 100644 --- a/c/socket/socket.go +++ b/c/socket/socket.go @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package socket import ( diff --git a/cpp/inih/READEME.md b/cpp/inih/READEME.md index f679f1da..93ef8a45 100644 --- a/cpp/inih/READEME.md +++ b/cpp/inih/READEME.md @@ -1 +1,14 @@ -brew install inih \ No newline at end of file +LLGo wrapper of benhoyt/inih +===== + +## How to install + +### on macOS (Homebrew) + +```sh +brew install inih +``` + +### on Linux (Debian/Ubuntu) + +TODO diff --git a/cpp/inih/_demo/inihdemo/ReaderDemo.go b/cpp/inih/_demo/inihdemo/ReaderDemo.go deleted file mode 100644 index 6ada6523..00000000 --- a/cpp/inih/_demo/inihdemo/ReaderDemo.go +++ /dev/null @@ -1,61 +0,0 @@ -package main - -import ( - "github.com/goplus/llgo/c" - "github.com/goplus/llgo/cpp/inih" -) - -/* -expected output: - section: owner name: name value: John Doe - section: owner name: organization value: Acme Widgets Inc. - section: database name: server value: 192.0.2.62 - section: database name: port value: 143 - section: database name: file value: "payroll.dat" - section: database name: enabled value: true - section: owner name: name value: John Doe - section: owner name: organization value: Acme Widgets Inc. - section: database name: server value: 192.0.2.62 - section: database name: port value: 143 - section: database name: file value: "payroll.dat" - section: database name: enabled value: true - section: settings name: username value: lee - section: settings name: timeout value: 20 - Config file parsed successfully - s : 192.0.2.62 - isDatabaseEnabled: true port: 143 - 0x109f54da0 - 0 - value: 100 -*/ - -func main() { - demo1() - reader := inih.NewReaderFile(inih.Str("config.ini")) - if reader.ParseError() != 0 { - println("Error parsing config file") - return - } - isDatabaseEnabled := reader.GetBoolean(inih.Str("database"), inih.Str("enabled"), false) - port := reader.GetInteger(inih.Str("database"), inih.Str("port"), 0) - s := reader.GetString(inih.Str("database"), inih.Str("server"), inih.Str("unknown")) - println("s :", s.String()) - println("isDatabaseEnabled:", isDatabaseEnabled, "port:", port) - demo2() - -} - -func demo2() { - buf := `[settings] -username=admin -timeout=100 -` - reader := inih.NewReader(c.Str(buf), c.Ulong(len(buf))) - - println(&reader) - println(reader.ParseError()) - sec := inih.Str("settings") - name := inih.Str("timeout") - value := reader.GetInteger(sec, name, 0) - println("value:", value) -} diff --git a/cpp/inih/_demo/inihdemo/inihdemo.go b/cpp/inih/_demo/inihdemo/inihdemo.go index 156cfff3..1de2af69 100644 --- a/cpp/inih/_demo/inihdemo/inihdemo.go +++ b/cpp/inih/_demo/inihdemo/inihdemo.go @@ -5,7 +5,7 @@ import ( "github.com/goplus/llgo/cpp/inih" ) -func demo1() { +func main() { filename := c.Str("config.ini") if inih.Parse(filename, func(user c.Pointer, section *c.Char, name *c.Char, value *c.Char) c.Int { diff --git a/cpp/inih/_demo/inihreader/config.ini b/cpp/inih/_demo/inihreader/config.ini new file mode 100644 index 00000000..62e16fc7 --- /dev/null +++ b/cpp/inih/_demo/inihreader/config.ini @@ -0,0 +1,10 @@ +; example.ini +[owner] +name = John Doe +organization = Acme Widgets Inc. + +[database] +server = 192.0.2.62 +port = 143 +file = "payroll.dat" +enabled = true diff --git a/cpp/inih/_demo/inihreader/reader_demo.go b/cpp/inih/_demo/inihreader/reader_demo.go new file mode 100644 index 00000000..13fbbcf6 --- /dev/null +++ b/cpp/inih/_demo/inihreader/reader_demo.go @@ -0,0 +1,31 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/cpp/inih" + "github.com/goplus/llgo/cpp/std" +) + +func main() { + buf := `[settings] +username=admin +timeout=100 +` + reader := inih.NewReader(c.Str(buf), uintptr(len(buf))) + println(reader.ParseError()) + sec := std.Str("settings") + name := std.Str("timeout") + value := reader.GetInteger(sec, name, 0) + println("value:", value) + + reader = inih.NewReaderFile(std.Str("config.ini")) + if ret := reader.ParseError(); ret != 0 { + println("Error parsing config file:", ret) + return + } + isDatabaseEnabled := reader.GetBoolean(std.Str("database"), std.Str("enabled"), false) + port := reader.GetInteger(std.NewString("database"), std.Str("port"), 0) + s := reader.GetString(std.Str("database"), std.Str("server"), std.Str("unknown")) + println("s:", s.Str()) + println("isDatabaseEnabled:", isDatabaseEnabled, "port:", port) +} diff --git a/cpp/inih/inih.go b/cpp/inih/inih.go index 05f5c384..08f96fd9 100644 --- a/cpp/inih/inih.go +++ b/cpp/inih/inih.go @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package inih import ( @@ -7,7 +23,7 @@ import ( ) const ( - LLGoPackage = "link: $(pkg-config --libs inih INIReader) -lc++; -linih -lINIReader" + LLGoPackage = "link: $(pkg-config --libs inih INIReader); -linih -lINIReader" ) //go:linkname Parse C.ini_parse diff --git a/cpp/inih/inihReader.go b/cpp/inih/inihReader.go deleted file mode 100644 index b1b682d1..00000000 --- a/cpp/inih/inihReader.go +++ /dev/null @@ -1,64 +0,0 @@ -package inih - -import ( - "unsafe" - - "github.com/goplus/llgo/c" -) - -// llgo:type C -type Reader struct { - Unused [24]byte -} - -type StdString struct { - buf [24]byte -} - -type __long struct { - __cap_ int - __size_ int - __data_ unsafe.Pointer -} - -func Str(s string) *StdString { - var r StdString - r.init(c.GoStringData(s), c.Int(len(s))) - return &r -} - -func (r *StdString) String() string { - if r.buf[0]&1 == 0 { - return c.GoString((*c.Char)(unsafe.Pointer(&r.buf[1]))) - } else { - v := *(*__long)(unsafe.Pointer(&r.buf[0])) - return unsafe.String((*byte)(v.__data_), v.__size_) - } -} - -// llgo:link (*StdString).init C._ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm -func (*StdString) init(s *c.Char, size c.Int) {} - -//go:linkname NewReader C._ZN9INIReaderC1EPKcm -func NewReader(fileName *c.Char, size c.Ulong) Reader - -//go:linkname NewReaderFile C._ZN9INIReaderC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE -func NewReaderFile(fileName *StdString) Reader - -// llgo:link (*Reader).ParseError C._ZNK9INIReader10ParseErrorEv -func (*Reader) ParseError() c.Int { return 0 } - -// llgo:link (*Reader).GetInteger C._ZNK9INIReader10GetIntegerERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_l -func (*Reader) GetInteger(section *StdString, name *StdString, defaultValue c.Long) c.Long { - return 0 -} - -// llgo:link (*Reader).GetBoolean C._ZNK9INIReader10GetBooleanERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_b -func (*Reader) GetBoolean(section *StdString, name *StdString, defaultValue bool) bool { - return false -} - -// llgo:link (*Reader).GetString C._ZNK9INIReader9GetStringERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_S8_ -func (*Reader) GetString(section *StdString, name *StdString, defaultValue *StdString) StdString { - return StdString{} -} diff --git a/cpp/inih/reader.go b/cpp/inih/reader.go new file mode 100644 index 00000000..89456ec0 --- /dev/null +++ b/cpp/inih/reader.go @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package inih + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/cpp/std" +) + +// ----------------------------------------------------------------------------- + +// llgo:type C +type Reader struct { + Unused [24]byte +} + +// llgo:link (*Reader).InitFromBuffer C._ZN9INIReaderC1EPKcm +func (r *Reader) InitFromBuffer(buffer *c.Char, bufferSize uintptr) {} + +// llgo:link (*Reader).InitFromFile C._ZN9INIReaderC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE +func (r *Reader) InitFromFile(buffer *c.Char, bufferSize uintptr) {} + +// ----------------------------------------------------------------------------- + +func NewReader(buffer *c.Char, bufferSize uintptr) (ret Reader) { + ret.InitFromBuffer(buffer, bufferSize) + return +} + +func NewReaderFile(fileName *std.String) (ret Reader) { + ret.InitFromFile(fileName.CStr(), fileName.Size()) + return +} + +// ----------------------------------------------------------------------------- + +// llgo:link (*Reader).ParseError C._ZNK9INIReader10ParseErrorEv +func (*Reader) ParseError() c.Int { return 0 } + +// ----------------------------------------------------------------------------- + +// llgo:link (*Reader).GetInteger C._ZNK9INIReader10GetIntegerERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_l +func (*Reader) GetInteger(section *std.String, name *std.String, defaultValue c.Long) c.Long { + return 0 +} + +// llgo:link (*Reader).GetBoolean C._ZNK9INIReader10GetBooleanERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_b +func (*Reader) GetBoolean(section *std.String, name *std.String, defaultValue bool) bool { + return false +} + +// llgo:link (*Reader).GetString C._ZNK9INIReader9GetStringERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_S8_ +func (*Reader) GetString(section *std.String, name *std.String, defaultValue *std.String) (ret std.String) { + return +} + +// ----------------------------------------------------------------------------- diff --git a/cpp/std/_cppstd/string.cpp b/cpp/std/_cppstd/string.cpp new file mode 100644 index 00000000..f48415e7 --- /dev/null +++ b/cpp/std/_cppstd/string.cpp @@ -0,0 +1,43 @@ +#include + +extern "C" { + +// ----------------------------------------------------------------------------- + +void stdStringInitEmpty(std::string* s) { + new(s) std::string(); +} + +void stdStringInitFrom(std::string* s, std::string* v) { + new(s) std::string(*v); +} + +void stdStringInitFromCStr(std::string* s, const char* cstr) { + new(s) std::string(cstr); +} + +void stdStringInitFromCStrLen(std::string* s, const char* cstr, size_t len) { + new(s) std::string(cstr, len); +} + +void stdStringDispose(const std::string* s) { + s->~basic_string(); +} + +// ----------------------------------------------------------------------------- + +const char* stdStringCStr(const std::string* s) { + return s->c_str(); +} + +const char* stdStringData(const std::string* s) { + return s->data(); +} + +size_t stdStringSize(const std::string* s) { + return s->size(); +} + +// ----------------------------------------------------------------------------- + +} // extern "C" diff --git a/cpp/std/_demo/cpphello/cppstr.go b/cpp/std/_demo/cpphello/cppstr.go new file mode 100644 index 00000000..55ed56a1 --- /dev/null +++ b/cpp/std/_demo/cpphello/cppstr.go @@ -0,0 +1,12 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/cpp/std" +) + +func main() { + s := std.Str("Hello world\n") + c.Printf(s.CStr()) + print(s.Str(), s.Size(), "\n") +} diff --git a/cpp/std/std.go b/cpp/std/std.go new file mode 100644 index 00000000..89d6b720 --- /dev/null +++ b/cpp/std/std.go @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package std + +const ( + LLGoFiles = "_cppstd/string.cpp" + LLGoPackage = "link: c++" +) + +// ----------------------------------------------------------------------------- diff --git a/cpp/std/string.go b/cpp/std/string.go new file mode 100644 index 00000000..fbc24bd9 --- /dev/null +++ b/cpp/std/string.go @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package std + +import ( + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/bdwgc" +) + +// ----------------------------------------------------------------------------- + +// String represents a C++ std::string object. +type String struct { + Unused [24]byte +} + +// llgo:link (*String).InitEmpty C.stdStringInitEmpty +func (s *String) InitEmpty() {} + +// llgo:link (*String).InitFrom C.stdStringInitFrom +func (s *String) InitFrom(v *String) {} + +// llgo:link (*String).InitFromCStr C.stdStringInitFromCStr +func (s *String) InitFromCStr(cstr *c.Char) {} + +// llgo:link (*String).InitFromCStrLen C.stdStringInitFromCStrLen +func (s *String) InitFromCStrLen(cstr *c.Char, n uintptr) {} + +// llgo:link (*String).Dispose C.stdStringDispose +func (s *String) Dispose() {} + +// ----------------------------------------------------------------------------- + +func allocString() *String { + ptr := bdwgc.Malloc(unsafe.Sizeof(String{})) + bdwgc.RegisterFinalizer(ptr, func(obj, data c.Pointer) { + (*String)(obj).Dispose() + }, nil, nil, nil) + return (*String)(ptr) +} + +// NewString creates a C++ std::string object. +func NewString(v string) *String { + ret := allocString() + ret.InitFromCStrLen(c.GoStringData(v), uintptr(len(v))) + return ret +} + +// NewStringEmpty creates an empty std::string object. +func NewStringEmpty() *String { + ret := allocString() + ret.InitEmpty() + return ret +} + +// NewStringFrom creates a copy of a C++ std::string object. +func NewStringFrom(v *String) *String { + ret := allocString() + ret.InitFrom(v) + return ret +} + +// NewStringFromCStr creates a C++ std::string object. +func NewStringFromCStr(cstr *c.Char) *String { + ret := allocString() + ret.InitFromCStr(cstr) + return ret +} + +// NewStringFromCStrLen creates a C++ std::string object. +func NewStringFromCStrLen(cstr *c.Char, n uintptr) *String { + ret := allocString() + ret.InitFromCStrLen(cstr, n) + return ret +} + +// ----------------------------------------------------------------------------- + +// Str returns a Go string (it doesn't clone data of the C++ std::string object). +func (s *String) Str() string { + return unsafe.String((*byte)(unsafe.Pointer(s.Data())), s.Size()) +} + +// llgo:link (*String).CStr C.stdStringCStr +func (s *String) CStr() *c.Char { return nil } + +// llgo:link (*String).Data C.stdStringData +func (s *String) Data() *c.Char { return nil } + +// llgo:link (*String).Size C.stdStringSize +func (s *String) Size() uintptr { return 0 } + +// ----------------------------------------------------------------------------- + +// GoString converts a C++ std::string object to a Go string. +func GoString(v *String) string { + return c.GoString(v.Data(), v.Size()) +} + +// Str creates a constant C++ std::string object. +func Str(v string) *String { + return NewString(v) // TODO(xsw): optimize it +} + +// ----------------------------------------------------------------------------- From b026bfc71b696b66f41282386e6b77f353c51f49 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 13 Jul 2024 17:57:46 +0800 Subject: [PATCH 2/4] inih.Reader: InitFromFile --- cpp/inih/reader.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/inih/reader.go b/cpp/inih/reader.go index 89456ec0..083edbd5 100644 --- a/cpp/inih/reader.go +++ b/cpp/inih/reader.go @@ -34,7 +34,7 @@ type Reader struct { func (r *Reader) InitFromBuffer(buffer *c.Char, bufferSize uintptr) {} // llgo:link (*Reader).InitFromFile C._ZN9INIReaderC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE -func (r *Reader) InitFromFile(buffer *c.Char, bufferSize uintptr) {} +func (r *Reader) InitFromFile(fileName *std.String) {} // ----------------------------------------------------------------------------- @@ -44,7 +44,7 @@ func NewReader(buffer *c.Char, bufferSize uintptr) (ret Reader) { } func NewReaderFile(fileName *std.String) (ret Reader) { - ret.InitFromFile(fileName.CStr(), fileName.Size()) + ret.InitFromFile(fileName) return } From af54a22d16f17e51193020b44a984577640c1d67 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 13 Jul 2024 18:11:34 +0800 Subject: [PATCH 3/4] inih: INIReaderDispose --- cpp/inih/_demo/inihreader/reader_demo.go | 4 ++++ cpp/inih/_wrap/reader.cpp | 13 +++++++++++++ cpp/inih/inih.go | 3 ++- cpp/inih/reader.go | 3 +++ cpp/std/{_cppstd => _wrap}/string.cpp | 2 +- cpp/std/std.go | 2 +- 6 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 cpp/inih/_wrap/reader.cpp rename cpp/std/{_cppstd => _wrap}/string.cpp (95%) diff --git a/cpp/inih/_demo/inihreader/reader_demo.go b/cpp/inih/_demo/inihreader/reader_demo.go index 13fbbcf6..3fe5d607 100644 --- a/cpp/inih/_demo/inihreader/reader_demo.go +++ b/cpp/inih/_demo/inihreader/reader_demo.go @@ -12,6 +12,8 @@ username=admin timeout=100 ` reader := inih.NewReader(c.Str(buf), uintptr(len(buf))) + defer reader.Dispose() + println(reader.ParseError()) sec := std.Str("settings") name := std.Str("timeout") @@ -19,6 +21,8 @@ timeout=100 println("value:", value) reader = inih.NewReaderFile(std.Str("config.ini")) + defer reader.Dispose() + if ret := reader.ParseError(); ret != 0 { println("Error parsing config file:", ret) return diff --git a/cpp/inih/_wrap/reader.cpp b/cpp/inih/_wrap/reader.cpp new file mode 100644 index 00000000..f7d2c099 --- /dev/null +++ b/cpp/inih/_wrap/reader.cpp @@ -0,0 +1,13 @@ +#include + +extern "C" { + +// ----------------------------------------------------------------------------- + +void INIReaderDispose(INIReader* r) { + r->~INIReader(); +} + +// ----------------------------------------------------------------------------- + +} // extern "C" diff --git a/cpp/inih/inih.go b/cpp/inih/inih.go index 08f96fd9..fe8a5214 100644 --- a/cpp/inih/inih.go +++ b/cpp/inih/inih.go @@ -23,7 +23,8 @@ import ( ) const ( - LLGoPackage = "link: $(pkg-config --libs inih INIReader); -linih -lINIReader" + LLGoFiles = "_wrap/reader.cpp" + LLGoPackage = "link: $(pkg-config --cflags --libs inih INIReader); -linih -lINIReader" ) //go:linkname Parse C.ini_parse diff --git a/cpp/inih/reader.go b/cpp/inih/reader.go index 083edbd5..987f3f48 100644 --- a/cpp/inih/reader.go +++ b/cpp/inih/reader.go @@ -36,6 +36,9 @@ func (r *Reader) InitFromBuffer(buffer *c.Char, bufferSize uintptr) {} // llgo:link (*Reader).InitFromFile C._ZN9INIReaderC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE func (r *Reader) InitFromFile(fileName *std.String) {} +// llgo:link (*Reader).Dispose C.INIReaderDispose +func (s *Reader) Dispose() {} + // ----------------------------------------------------------------------------- func NewReader(buffer *c.Char, bufferSize uintptr) (ret Reader) { diff --git a/cpp/std/_cppstd/string.cpp b/cpp/std/_wrap/string.cpp similarity index 95% rename from cpp/std/_cppstd/string.cpp rename to cpp/std/_wrap/string.cpp index f48415e7..94f73508 100644 --- a/cpp/std/_cppstd/string.cpp +++ b/cpp/std/_wrap/string.cpp @@ -20,7 +20,7 @@ void stdStringInitFromCStrLen(std::string* s, const char* cstr, size_t len) { new(s) std::string(cstr, len); } -void stdStringDispose(const std::string* s) { +void stdStringDispose(std::string* s) { s->~basic_string(); } diff --git a/cpp/std/std.go b/cpp/std/std.go index 89d6b720..d36a2e09 100644 --- a/cpp/std/std.go +++ b/cpp/std/std.go @@ -17,7 +17,7 @@ package std const ( - LLGoFiles = "_cppstd/string.cpp" + LLGoFiles = "_wrap/string.cpp" LLGoPackage = "link: c++" ) From a3ea4798bc0f85350945289fd65506d7be265a22 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 13 Jul 2024 18:55:09 +0800 Subject: [PATCH 4/4] build: clFiles support cflags, reuse llvm.Env; cpp/inih: _wrap/reader.cpp --- cpp/inih/_demo/inihreader/reader_demo.go | 11 +++++-- cpp/inih/inih.go | 4 +-- cpp/inih/reader.go | 2 ++ internal/build/build.go | 42 +++++++++++++++--------- 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/cpp/inih/_demo/inihreader/reader_demo.go b/cpp/inih/_demo/inihreader/reader_demo.go index 3fe5d607..874a9950 100644 --- a/cpp/inih/_demo/inihreader/reader_demo.go +++ b/cpp/inih/_demo/inihreader/reader_demo.go @@ -6,7 +6,7 @@ import ( "github.com/goplus/llgo/cpp/std" ) -func main() { +func demoFromBuffer() { buf := `[settings] username=admin timeout=100 @@ -19,8 +19,10 @@ timeout=100 name := std.Str("timeout") value := reader.GetInteger(sec, name, 0) println("value:", value) +} - reader = inih.NewReaderFile(std.Str("config.ini")) +func demoFromFile() { + reader := inih.NewReaderFile(std.Str("config.ini")) defer reader.Dispose() if ret := reader.ParseError(); ret != 0 { @@ -33,3 +35,8 @@ timeout=100 println("s:", s.Str()) println("isDatabaseEnabled:", isDatabaseEnabled, "port:", port) } + +func main() { + demoFromBuffer() + demoFromFile() +} diff --git a/cpp/inih/inih.go b/cpp/inih/inih.go index fe8a5214..adac5836 100644 --- a/cpp/inih/inih.go +++ b/cpp/inih/inih.go @@ -23,8 +23,8 @@ import ( ) const ( - LLGoFiles = "_wrap/reader.cpp" - LLGoPackage = "link: $(pkg-config --cflags --libs inih INIReader); -linih -lINIReader" + LLGoFiles = "$(pkg-config --cflags INIReader): _wrap/reader.cpp" + LLGoPackage = "link: $(pkg-config --libs inih INIReader); -linih -lINIReader" ) //go:linkname Parse C.ini_parse diff --git a/cpp/inih/reader.go b/cpp/inih/reader.go index 987f3f48..ee194e77 100644 --- a/cpp/inih/reader.go +++ b/cpp/inih/reader.go @@ -41,11 +41,13 @@ func (s *Reader) Dispose() {} // ----------------------------------------------------------------------------- +// NewReader creates a new INIReader instance. func NewReader(buffer *c.Char, bufferSize uintptr) (ret Reader) { ret.InitFromBuffer(buffer, bufferSize) return } +// NewReaderFile creates a new INIReader instance. func NewReaderFile(fileName *std.String) (ret Reader) { ret.InitFromFile(fileName) return diff --git a/internal/build/build.go b/internal/build/build.go index 8913a0cb..4cc903a0 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -174,7 +174,7 @@ func Do(args []string, conf *Config) { patches := make(cl.Patches, len(altPkgPaths)) altSSAPkgs(progSSA, patches, altPkgs[1:], verbose) - ctx := &context{progSSA, prog, dedup, patches, make(map[string]none), initial, mode} + ctx := &context{llvm.New(""), progSSA, prog, dedup, patches, make(map[string]none), initial, mode} pkgs := buildAllPkgs(ctx, initial, verbose) var llFiles []string @@ -189,7 +189,7 @@ func Do(args []string, conf *Config) { nErr := 0 for _, pkg := range initial { if pkg.Name == "main" { - nErr += linkMainPkg(pkg, pkgs, llFiles, conf, mode, verbose) + nErr += linkMainPkg(ctx, pkg, pkgs, llFiles, conf, mode, verbose) } } if nErr > 0 { @@ -221,6 +221,7 @@ const ( ) type context struct { + env *llvm.Env progSSA *ssa.Program prog llssa.Program dedup packages.Deduper @@ -256,13 +257,14 @@ func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs case cl.PkgLinkIR, cl.PkgLinkExtern, cl.PkgPyModule: if len(pkg.GoFiles) > 0 { buildPkg(ctx, aPkg, verbose) - pkg.ExportFile = " " + concatPkgLinkFiles(pkg, verbose) + " " + pkg.ExportFile + pkg.ExportFile = " " + concatPkgLinkFiles(ctx, pkg, verbose) + " " + pkg.ExportFile } else { // panic("todo") // TODO(xsw): support packages out of llgo pkg.ExportFile = "" } - if kind == cl.PkgLinkExtern { // need to be linked with external library + if kind == cl.PkgLinkExtern { + // need to be linked with external library // format: ';' separated alternative link methods. e.g. // link: $LLGO_LIB_PYTHON; $(pkg-config --libs python3-embed); -lpython3 expd := "" @@ -305,7 +307,7 @@ func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs return } -func linkMainPkg(pkg *packages.Package, pkgs []*aPackage, llFiles []string, conf *Config, mode Mode, verbose bool) (nErr int) { +func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, llFiles []string, conf *Config, mode Mode, verbose bool) (nErr int) { pkgPath := pkg.PkgPath name := path.Base(pkgPath) app := conf.OutFile @@ -390,7 +392,7 @@ func linkMainPkg(pkg *packages.Package, pkgs []*aPackage, llFiles []string, conf if verbose { fmt.Fprintln(os.Stderr, "clang", args) } - err := llvm.New("").Clang().Exec(args...) + err := ctx.env.Clang().Exec(args...) check(err) switch mode { @@ -589,6 +591,7 @@ func appendLinkFiles(args []string, file string) []string { if isSingleLinkFile(file) { return append(args, file) } + // TODO(xsw): consider filename with spaces return append(args, strings.Split(file[1:], " ")...) } @@ -596,11 +599,11 @@ func isSingleLinkFile(ret string) bool { return len(ret) > 0 && ret[0] != ' ' } -func concatPkgLinkFiles(pkg *packages.Package, verbose bool) string { +func concatPkgLinkFiles(ctx *context, pkg *packages.Package, verbose bool) string { var b strings.Builder var ret string var n int - llgoPkgLinkFiles(pkg, func(linkFile string) { + llgoPkgLinkFiles(ctx, pkg, func(linkFile string) { if n == 0 { ret = linkFile } else { @@ -618,32 +621,41 @@ func concatPkgLinkFiles(pkg *packages.Package, verbose bool) string { } // const LLGoFiles = "file1; file2; ..." -func llgoPkgLinkFiles(pkg *packages.Package, procFile func(linkFile string), verbose bool) { +func llgoPkgLinkFiles(ctx *context, pkg *packages.Package, procFile func(linkFile string), verbose bool) { if o := pkg.Types.Scope().Lookup("LLGoFiles"); o != nil { val := o.(*types.Const).Val() if val.Kind() == constant.String { - clFiles(constant.StringVal(val), pkg, procFile, verbose) + clFiles(ctx, constant.StringVal(val), pkg, procFile, verbose) } } } // files = "file1; file2; ..." -func clFiles(files string, pkg *packages.Package, procFile func(linkFile string), verbose bool) { +// files = "$(pkg-config --cflags xxx): file1; file2; ..." +func clFiles(ctx *context, files string, pkg *packages.Package, procFile func(linkFile string), verbose bool) { dir := filepath.Dir(pkg.GoFiles[0]) expFile := pkg.ExportFile + args := make([]string, 0, 16) + if strings.HasPrefix(files, "$") { // has cflags + if pos := strings.IndexByte(files, ':'); pos > 0 { + cflags := env.ExpandEnv(files[:pos]) + files = files[pos+1:] + args = append(args, strings.Split(cflags, " ")...) + } + } for _, file := range strings.Split(files, ";") { cFile := filepath.Join(dir, strings.TrimSpace(file)) - clFile(cFile, expFile, procFile, verbose) + clFile(ctx, args, cFile, expFile, procFile, verbose) } } -func clFile(cFile, expFile string, procFile func(linkFile string), verbose bool) { +func clFile(ctx *context, args []string, cFile, expFile string, procFile func(linkFile string), verbose bool) { llFile := expFile + filepath.Base(cFile) + ".ll" - args := []string{"-emit-llvm", "-S", "-o", llFile, "-c", cFile} + args = append(args, "-emit-llvm", "-S", "-o", llFile, "-c", cFile) if verbose { fmt.Fprintln(os.Stderr, "clang", args) } - err := llvm.New("").Clang().Exec(args...) + err := ctx.env.Clang().Exec(args...) check(err) procFile(llFile) }