Repository: google/benchmark
File: bindings/python/google_benchmark/benchmark.cc (C++)

Code before:
#include "benchmark/benchmark.h"

#include "nanobind/nanobind.h"
#include "nanobind/operators.h"
#include "nanobind/stl/bind_map.h"
#include "nanobind/stl/string.h"
#include "nanobind/stl/vector.h"

NB_MAKE_OPAQUE(benchmark::UserCounters);

namespace {
namespace nb = nanobind;

std::vector<std::string> Initialize(const std::vector<std::string>& argv) {
  std::vector<char*> ptrs;
  ptrs.reserve(argv.size());
  for (auto& arg : argv) {
    ptrs.push_back(const_cast<char*>(arg.c_str()));
  }
  if (!ptrs.empty()) {
    // The `argv` pointers here become invalid when this function returns, but
    // benchmark holds the pointer to `argv[0]`. We create a static copy of it
    // so it persists, and replace the pointer below.
    static std::string executable_name(argv[0]);
    ptrs[0] = const_cast<char*>(executable_name.c_str());
  }
  int argc = static_cast<int>(argv.size());
  benchmark::Initialize(&argc, ptrs.data());
  std::vector<std::string> remaining_argv;
  remaining_argv.reserve(argc);
  for (int i = 0; i < argc; ++i) {
    remaining_argv.emplace_back(ptrs[i]);
  }
  return remaining_argv;
}

benchmark::internal::Benchmark* RegisterBenchmark(const std::string& name,
                                                  nb::callable f) {
  return benchmark::RegisterBenchmark(
      name, [f](benchmark::State& state) { f(&state); });
}

NB_MODULE(_benchmark, m) {

  using benchmark::TimeUnit;
  nb::enum_<TimeUnit>(m, "TimeUnit")
      .value("kNanosecond", TimeUnit::kNanosecond)
      .value("kMicrosecond", TimeUnit::kMicrosecond)
      .value("kMillisecond", TimeUnit::kMillisecond)
      .value("kSecond", TimeUnit::kSecond)
      .export_values();

Review comment (bug):
Doesn't this just exchange one bug for another?
The comment suggests that something uses the binary name (`argv[0]`) later on,
but now instead of "crashing" here, we'll crash the code that tries to take `argv[0]`.

Code after:
#include "benchmark/benchmark.h"

#include "nanobind/nanobind.h"
#include "nanobind/operators.h"
#include "nanobind/stl/bind_map.h"
#include "nanobind/stl/string.h"
#include "nanobind/stl/vector.h"

NB_MAKE_OPAQUE(benchmark::UserCounters);

namespace {
namespace nb = nanobind;

std::vector<std::string> Initialize(const std::vector<std::string>& argv) {
  std::vector<char*> ptrs;
  ptrs.reserve(argv.size());
  for (auto& arg : argv) {
    ptrs.push_back(const_cast<char*>(arg.c_str()));
  }
  if (!ptrs.empty()) {
    // The `argv` pointers here become invalid when this function returns, but
    // benchmark holds the pointer to `argv[0]`. We create a static copy of it
    // so it persists, and replace the pointer below.
    static std::string executable_name(argv[0]);
    ptrs[0] = const_cast<char*>(executable_name.c_str());
  }
  int argc = static_cast<int>(argv.size());
  benchmark::Initialize(&argc, ptrs.data());
  std::vector<std::string> remaining_argv;
  remaining_argv.reserve(argc);
  for (int i = 0; i < argc; ++i) {
    remaining_argv.emplace_back(ptrs[i]);
  }
  return remaining_argv;
}

benchmark::internal::Benchmark* RegisterBenchmark(const std::string& name,
                                                  nb::callable f) {
  return benchmark::RegisterBenchmark(
      name, [f](benchmark::State& state) { f(&state); });
}

NB_MODULE(_benchmark, m) {
  using benchmark::TimeUnit;
  nb::enum_<TimeUnit>(m, "TimeUnit")
      .value("kNanosecond", TimeUnit::kNanosecond)
      .value("kMicrosecond", TimeUnit::kMicrosecond)
      .value("kMillisecond", TimeUnit::kMillisecond)
      .value("kSecond", TimeUnit::kSecond)
      .export_values();