0

I am trying to solve a problem where I can define a class given an std::string only once, or else the compiler will throw an error.

Example:

Suppose we have a class Car which is initialized with its license plate. When creating it, the compiler is supposed to check if a Car instance with the exact same license plate string has already been created before.

My approaches so far:

I have been looking around for template-metaprogramming solutions which should create a simple registerable counter (example), however I fear that this doesn't really fit my needs.

Another idea was to create a simple define including the given license plate string, which but of course are unable to be created at compile-time since the license plate string is being passed only at run-time.

#include <string>
#include <vector>
#include <algorithm>
#include <iostream>

std::vector<std::string> myGlobalVector {};

class Car {
public:
  Car(std::string const& licensePlate);
  ~Car() = default;
  // ...

private:
    std::string _plate;
};

// ...

Car::Car(std::string const& licensePlate)
{
  // this would be the runtime version of what I want to achieve:
  const bool alreadyExists = std::any_of(
        myGlobalVector.begin(), 
        myGlobalVector.end(), 
        [&licensePlate](std::string const& otherPlate)
        {
            return otherPlate == licensePlate;
        });

    if (alreadyExists)
    {
        std::cerr << "License plate already registered. Exiting." << std::endl;
        exit(-1);
    }

    myGlobalVector.emplace(licensePlate);
}

int main() {
    Car someCar { "A4EM21F" };
    Car anotherCar { "F121EG4" };

    // ...

    // this should throw a compile-time error as 
    // given string has already been used before in this context
    Car lastCar { "A4EM21F" };
}

My only idea so far (which obviously won't compile but is supposed to illustrate what I want to achieve):

// ...
Car::Car(std::string const& licensePlate)
{
#ifndef CAR_##licensePlate
#define CAR_##licensePlate
    _plate = licensePlate;
#else
#error Car has already been created in your code!
#endif
}
// ...

Can anyone think of a way to check the occurrence of a string in a code snippet at compile-time?

I woult be grateful for helpful hints using any kinds of template-metaprogramming, type-trait or other topics.

tai
  • 477
  • 1
  • 5
  • 16
  • Make a std::vector static member for the class and whenever a new object is instanciated, you check if there a car with the same plate in the vector and if not, add it to the list. Would that work? – ihsan Oct 27 '21 at 13:07
  • 1
    You certainly can’t do this across translation units; is it worthwhile otherwise? – Davis Herring Oct 27 '21 at 13:09
  • With `std::string_view` instead of `std::string`, you might ensure at compile-time uniqueness of `std::array MakeCar()`. "Issue" is to pass C-string as argument simply. (gcc/clang has extension which allow to simply construct char sequence from C-strings literal). – Jarod42 Oct 27 '21 at 13:13
  • I'd prefer `static std::set`. – sweenish Oct 27 '21 at 13:18
  • I think you can find use of a design pattern called singleton if I understood your question correctly. – Karen Baghdasaryan Oct 27 '21 at 13:34
  • @KarenBaghdasaryan How does this help me to make a compile-time string occurrency check? – tai Oct 28 '21 at 07:35
  • @sweenish same question for you - how does this help me to make a compile-time string occurency check? – tai Oct 28 '21 at 07:36
  • @DavisHerring true, however it's still worthwile in this case. – tai Oct 28 '21 at 07:36
  • @ihsan This doesn't work at **compile-time**. – tai Oct 28 '21 at 07:36
  • If your license plates are not all known at compile time, you can't. And if they were, the list should probably already be filtered. Why does it need to be compile time, and is that even how your program would work? This doesn't sound too different from asking how a game can take user input at compile time. – sweenish Oct 28 '21 at 12:40
  • @sweenish I can't get too much into detail here, however it's supposed to be a double-check for the developers to make sure they don't use the same *license plate* twice in the code. that's what I want to achieve. if they still do, they'll be warned by the compiler. – tai Nov 01 '21 at 06:51
  • Completely unnecessary with a static set. – sweenish Nov 01 '21 at 15:05
  • @sweenish could you please explain this to me? tbh I usually don't work with sets so I can't see how a static set would help me with this. – tai Nov 01 '21 at 15:57
  • One would have hoped that my earlier comment made last week would have sparked some curiosity and googling "std::set" would land you on [this](https://en.cppreference.com/w/cpp/container/set) page. Then reading the first sentence gives you "std::set is an associative container that contains a sorted set of **unique** objects of type Key." (emphasis added). Being static makes the same container accessible to all objects of the class. It's not compile time, but that requirement makes no sense. – sweenish Nov 01 '21 at 16:02
  • @sweenish your comment is absolutely not helpful. I was looking for a **compile-time** solution, not a random comment telling me how my idea wouldn't make sense without even knowing how my environment looks like. if I was looking for a runtime solution, I would have certainly run into sets or unordered maps before, but that was not what I was asking for. – tai Nov 05 '21 at 15:33
  • Refer to the only answer you have. – sweenish Nov 05 '21 at 19:49

1 Answers1

1

In C++ 20 you can use the constexpr versions of the containers and with that implement your solution. But, then everything has to be constexpr. Not very meaningful.

I do not exactly know, what you want to achieve, but your design may be broken.

A M
  • 14,694
  • 5
  • 19
  • 44
  • It might indeed be, however the design unfortunately is not my main problem as I need to implement this solution for a big piece of software at my workplace. using constexpr was already in my mind, however for multiple reasons that's not possible for me. – tai Nov 04 '21 at 18:42