Reputation: 5185
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
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
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
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