rust-cpp icon indicating copy to clipboard operation
rust-cpp copied to clipboard

Rust static variables as C++ non-type template parameter

Open ffranr opened this issue 4 years ago • 4 comments

Hello!

I'm trying to use a Rust static variable as a C++ non-type template parameter. Is this possible?

This is a simple example which outlines what I'm trying to do:

#[macro_use]
extern crate cpp;

cpp! {{
    #include <iostream>
    #include "src/tmp.h"
}}

static NUMBER: i32 = 42;

fn foo() {
    unsafe {
        cpp!([NUMBER as "int32_t"] {
            const int32_t replace_me_with_number = 3;
            std::cout << Add<replace_me_with_number>() << "   " << NUMBER << std::endl;
        })
    };
}

#[cfg(test)]
mod tests {
    use crate::foo;
    #[test]
    fn it_works() {
        foo();
    }
}

I'm trying to replace replace_me_with_number with NUMBER.

src/tmp.h is just:

template <int32_t N>
int Add()
{
    return N + 3;
}

Thank you for your help.

ffranr avatar May 24 '20 13:05 ffranr

This isn't valid C++ code. The same thing wouldn't compile in plain C++ either.

#include <cstdint>

template <int32_t N>
void Add() {}

static int32_t NUMBER = 42;

int main() {
  Add<NUMBER>();
}
$ clang++ main.cc
main.cc:9:3: error: no matching function for call to 'Add'
  Add<NUMBER>();
  ^~~~~~~~~~~
main.cc:4:6: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'N'
void Add() {}
     ^
$ g++ main.cc
main.cc: In function ‘int main()’:
main.cc:9:7: error: the value of ‘NUMBER’ is not usable in a constant expression
    9 |   Add<NUMBER>();
      |       ^~~~~~
main.cc:6:16: note: ‘int32_t NUMBER’ is not const
    6 | static int32_t NUMBER = 42;
      |                ^~~~~~
main.cc:9:15: error: no matching function for call to ‘Add<NUMBER>()’
    9 |   Add<NUMBER>();
      |               ^
main.cc:4:6: note: candidate: ‘template<int N> void Add()’
    4 | void Add() {}
      |      ^~~

dtolnay avatar May 24 '20 20:05 dtolnay

Thank you for the reply @dtolnay (btw, I love your work!)

I agree with you that the example C++ snippet that you have provided is not valid C++. However, I'm not trying to produce that sort of code. This is the sort of valid C++ that I hope to produce:

#include <cstdint>

template <int32_t N>
void Add() {}

static const int32_t NUMBER = 42;

int main() {
  Add<NUMBER>();
}

How can I pass a Rust variable (whose value is known at compile time) to the C++ side such that it can be used as a non-type template parameter?

ffranr avatar May 24 '20 21:05 ffranr

What is the real use case you have in mind for this feature? The C++ compilation is done before the rust compilation, and as such the C++ do not have access to anything that the rust compiler sees. Now, we could make them available to cpp_build.

For example, we could imagine an annotation such as:

#[cpp::export_static(as "uint32_t")]
static NUMBER: u32 = 42;

Which would tell cpp_build to generate constexpr uint32_t NUMBER = 42;

This would have the following drawback:

  • The cpp_build parser is not a full rust parser, and matching the #[cpp::export_static] might not work in 100% of the cases.
  • The right hand side of the = must also be a very simple expression that cpp_build can understand, so basically that would be limited to plain constant numbers.

ogoffart avatar May 26 '20 05:05 ogoffart

Thanks for the reply @ogoffart.

I think you're right that I need to take a step back and make sure my strategy makes sense. My ultimate goal is to build a Rust library which wraps the SDSL C++ library.

SDSL provides data structures such as the int_vector, where the elements of the vector have a predetermined set bit width:

template<uint8_t t_width>
class int_vector

There are other data structures which are a more complicated. Their templates are parameterized on types. For example the wavelet tree class for integer sequences:

template<class t_bitvector   = bit_vector,
         class t_rank        = typename t_bitvector::rank_1_type,
         class t_select      = typename t_bitvector::select_1_type,
         class t_select_zero = typename t_bitvector::select_0_type>
class wt_int

I think that what you've suggested above (#[cpp::export_static(as "uint32_t")]) might solve the most basic cases. And the drawbacks that you've suggested would be acceptable.

How do you think I should go about writing a wrapper for this library?

ffranr avatar May 26 '20 23:05 ffranr