ImaginaryHuman072889
ImaginaryHuman072889

Reputation: 5185

C++ Force Compile-Time Error Based on which Constructor is Called

In my C++ class, I want to prevent certain member functions from being able to be called depending on which constructor is used to create the object, and cause a compile-time error if this is attempted.

A bit of context:

I have an existing widely-used class in my project that is used to read/write to a database. Sometimes when I create an instance of this class, I only want to read from the database, and because of this, I want to create a new constructor which is a "read-only" constructor that prevents all functions that write to the database from doing anything. (My reason for doing this is because I want to "lock out" the row from the database being accessed if the object is going to write to it, but not do this if the object is only reading from the database.)

Of course, this is not very difficult to accomplish to prevent a member function from doing anything during run-time based on which constructor is used. For example:

class DatabaseInterface
{
   private:

      bool readOnly;

   public:

      // Constructor #1
      // This constructor allows read only from database
      DatabaseInterface(int x)
      {
         readOnly = true;

         // Do something
      }

      // Constructor #2
      // This constructor allows read/write to database
      DatabaseInterface(int x, bool y)
      {
         readOnly = false;

         // Do something
      }

      void UpdateDatabase()
      {
         if (readOnly) return;

         // Update Database
      }
};

With this class, you can do this:

DatabaseInterface A(5);
DatabaseInterface B(5,true);
A.UpdateDatabase();           // Does nothing
B.UpdateDatabase();           // Updates database

But is there a way to modify this class such that A.UpdateDatabase() above would cause a compile-time error?

My thinking is that (since all instances of my class currently use a constructor with one argument) if I can force a compile-time error for this, then I can ensure that all instances of the class will use the correct constructor, because it will not even compile if a "writing" function is attempted to be used on an object that was created with a "read-only" constructor.

I am wondering if this is possible because note that the value of the second argument in the constructor doesn't matter, as long as it is a bool type.

Upvotes: 0

Views: 555

Answers (3)

Slava
Slava

Reputation: 44238

You can use inheritance for this:

class ReadOnlyDBI {
public:
    void readData();
    virtual ~ReadOnlyDBI();
};

class ReadWriteDBI : public ReadOnlyDBI {
public:
    void writeData();
};


void someFunctionReadingData( ReadOnlyDBI &dbi )
{
    dbi.readData(); // fine
    dbi.writeData(); // error, this function can only read
}

void foo()
{
   ReadWriteDBI rwd;
   rwd.readData(); // fine to read from it
   someFunctionReadingData( rwd ); // we can pass it to a func that reads 
   rwd.writeData(); // we can write
}

otherwise you will have to write someFunctionReadingData as template as well.

Upvotes: 1

NathanOliver
NathanOliver

Reputation: 180415

If you are willing to make the class a template then you can use tags to make a read-only DatabaseInterface or a read-write DatabaseInterface. That would look like

struct read_only_t{};
struct read_write_t{};

template<typename T>
class DatabaseInterface
{
   public:
      DatabaseInterface(int x)
      {
         // Do something
      }

      void UpdateDatabase()
      {
         static_assert(!std::is_same_v<T, read_only_t>, "DatabaseInterface is in read only mode");
         // Update Database
      }
};

And now you get a nice compiler error if you try to call UpdateDatabase on a DatabaseInterface<read_only_t>

Upvotes: 4

PlinyTheElder
PlinyTheElder

Reputation: 1494

Yes, you can make it fail at compile time provided you are willing to template your DatabaseInterface class.

You'd need to add a template argument of type bool, for example, and then specialize the UpdateDatabase() function only for template parameter value of <true>. When you try to call the UpdateDatabase() function on an object where the parameter is <false>, the compiler will complain that the function is declared, but not defined.

Upvotes: 1

Related Questions