diff mbox series

[RFC,2/6] repository: add initialize_repo wrapper without pointer

Message ID 5f2e816cf6359725f2a86ce1d08e5e272fba4dac.1723054623.git.steadmon@google.com (mailing list archive)
State Superseded
Headers show
Series Introduce cgit-rs, a Rust wrapper around libgit.a | expand

Commit Message

Josh Steadmon Aug. 7, 2024, 6:21 p.m. UTC
Non-C external consumers of libgit.a have to redefine the `repository`
object in their own language if they want to call
initialize_repository() to ensure memory for the object is allocated
correctly. This is not ideal for external consumers that have no need
for the entire `the_repository` object but need to call other functions
from an initialized repository. Therefore, add a friendly
initialize_repository() wrapper without a `the_repository` pointer.

Co-authored-by: Calvin Wan <calvinwan@google.com>
Signed-off-by: Calvin Wan <calvinwan@google.com
Signed-off-by: Josh Steadmon <steadmon@google.com>
---
 repository.c | 9 +++++++++
 repository.h | 1 +
 2 files changed, 10 insertions(+)

Comments

Mike Hommey Aug. 7, 2024, 10:52 p.m. UTC | #1
On Wed, Aug 07, 2024 at 11:21:27AM -0700, Josh Steadmon wrote:
> Non-C external consumers of libgit.a have to redefine the `repository`
> object in their own language if they want to call
> initialize_repository() to ensure memory for the object is allocated
> correctly. This is not ideal for external consumers that have no need
> for the entire `the_repository` object but need to call other functions
> from an initialized repository. Therefore, add a friendly
> initialize_repository() wrapper without a `the_repository` pointer.

Technically speaking, you don't really need this.

You can define `repository` as an opaque type in Rust:
```
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct repository([u8; 0]);
```

And define `the_repository` as an extern symbol:
```
extern "C" {
    pub static mut the_repository: *mut repository;
}
```

Mike
Josh Steadmon Aug. 7, 2024, 11:23 p.m. UTC | #2
On 2024.08.08 07:52, Mike Hommey wrote:
> On Wed, Aug 07, 2024 at 11:21:27AM -0700, Josh Steadmon wrote:
> > Non-C external consumers of libgit.a have to redefine the `repository`
> > object in their own language if they want to call
> > initialize_repository() to ensure memory for the object is allocated
> > correctly. This is not ideal for external consumers that have no need
> > for the entire `the_repository` object but need to call other functions
> > from an initialized repository. Therefore, add a friendly
> > initialize_repository() wrapper without a `the_repository` pointer.
> 
> Technically speaking, you don't really need this.
> 
> You can define `repository` as an opaque type in Rust:
> ```
> #[allow(non_camel_case_types)]
> #[repr(C)]
> pub struct repository([u8; 0]);
> ```
> 
> And define `the_repository` as an extern symbol:
> ```
> extern "C" {
>     pub static mut the_repository: *mut repository;
> }
> ```
> 
> Mike

I've actually already done a refactor for V2 that will avoid using this
patch entirely, but thank you for the pointer. We do something similar
to opaquely wrap configset pointers in a later patch (we use an empty
enum there, I'm not sure whether that approach or a zero-size array is
preferred).
Mike Hommey Aug. 7, 2024, 11:29 p.m. UTC | #3
On Wed, Aug 07, 2024 at 04:23:05PM -0700, Josh Steadmon wrote:
> On 2024.08.08 07:52, Mike Hommey wrote:
> > On Wed, Aug 07, 2024 at 11:21:27AM -0700, Josh Steadmon wrote:
> > > Non-C external consumers of libgit.a have to redefine the `repository`
> > > object in their own language if they want to call
> > > initialize_repository() to ensure memory for the object is allocated
> > > correctly. This is not ideal for external consumers that have no need
> > > for the entire `the_repository` object but need to call other functions
> > > from an initialized repository. Therefore, add a friendly
> > > initialize_repository() wrapper without a `the_repository` pointer.
> > 
> > Technically speaking, you don't really need this.
> > 
> > You can define `repository` as an opaque type in Rust:
> > ```
> > #[allow(non_camel_case_types)]
> > #[repr(C)]
> > pub struct repository([u8; 0]);
> > ```
> > 
> > And define `the_repository` as an extern symbol:
> > ```
> > extern "C" {
> >     pub static mut the_repository: *mut repository;
> > }
> > ```
> > 
> > Mike
> 
> I've actually already done a refactor for V2 that will avoid using this
> patch entirely, but thank you for the pointer. We do something similar
> to opaquely wrap configset pointers in a later patch (we use an empty
> enum there, I'm not sure whether that approach or a zero-size array is
> preferred).

An empty enum is a never type, I wouldn't recommend using it as an opaque
wrapper. It will likely lead to the compiler doing bad things.
https://rust-lang.github.io/never-type-initiative/RFC.html

`#[repr(C)]` and `[u8; 0]` are recommended by the nomicon.
https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs

(the PhantomPinned wasn't there last time I saw that page)

Mike
Josh Steadmon Aug. 8, 2024, 5:50 p.m. UTC | #4
On 2024.08.08 08:29, Mike Hommey wrote:
> On Wed, Aug 07, 2024 at 04:23:05PM -0700, Josh Steadmon wrote:
> > On 2024.08.08 07:52, Mike Hommey wrote:
> > > On Wed, Aug 07, 2024 at 11:21:27AM -0700, Josh Steadmon wrote:
> > > > Non-C external consumers of libgit.a have to redefine the `repository`
> > > > object in their own language if they want to call
> > > > initialize_repository() to ensure memory for the object is allocated
> > > > correctly. This is not ideal for external consumers that have no need
> > > > for the entire `the_repository` object but need to call other functions
> > > > from an initialized repository. Therefore, add a friendly
> > > > initialize_repository() wrapper without a `the_repository` pointer.
> > > 
> > > Technically speaking, you don't really need this.
> > > 
> > > You can define `repository` as an opaque type in Rust:
> > > ```
> > > #[allow(non_camel_case_types)]
> > > #[repr(C)]
> > > pub struct repository([u8; 0]);
> > > ```
> > > 
> > > And define `the_repository` as an extern symbol:
> > > ```
> > > extern "C" {
> > >     pub static mut the_repository: *mut repository;
> > > }
> > > ```
> > > 
> > > Mike
> > 
> > I've actually already done a refactor for V2 that will avoid using this
> > patch entirely, but thank you for the pointer. We do something similar
> > to opaquely wrap configset pointers in a later patch (we use an empty
> > enum there, I'm not sure whether that approach or a zero-size array is
> > preferred).
> 
> An empty enum is a never type, I wouldn't recommend using it as an opaque
> wrapper. It will likely lead to the compiler doing bad things.
> https://rust-lang.github.io/never-type-initiative/RFC.html
> 
> `#[repr(C)]` and `[u8; 0]` are recommended by the nomicon.
> https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
> 
> (the PhantomPinned wasn't there last time I saw that page)
> 
> Mike

Fixed in V2 (for ConfigSet). Thanks!
diff mbox series

Patch

diff --git a/repository.c b/repository.c
index 9825a30899..15cb9ee735 100644
--- a/repository.c
+++ b/repository.c
@@ -81,6 +81,15 @@  void initialize_repository(struct repository *repo)
 		set_default_hash_algo(repo);
 }
 
+/*
+ * For non-C external consumers of libgit.a that do not need access
+ * to the entire `the_repository` object.
+ */
+void initialize_the_repository(void)
+{
+	initialize_repository(the_repository);
+}
+
 static void expand_base_dir(char **out, const char *in,
 			    const char *base_dir, const char *def_in)
 {
diff --git a/repository.h b/repository.h
index af6ea0a62c..65c9158866 100644
--- a/repository.h
+++ b/repository.h
@@ -227,6 +227,7 @@  void repo_set_compat_hash_algo(struct repository *repo, int compat_algo);
 void repo_set_ref_storage_format(struct repository *repo,
 				 enum ref_storage_format format);
 void initialize_repository(struct repository *repo);
+void initialize_the_repository(void);
 RESULT_MUST_BE_USED
 int repo_init(struct repository *r, const char *gitdir, const char *worktree);