-
-
Notifications
You must be signed in to change notification settings - Fork 74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Is there a way to reflect private fields? #66
Comments
@bboysnick5 yes, there are technically several possibilities to access class Weak {
private:
std::string name;
public:
void setName(const std::string& name) { this->name = name; }
std::string getName()const { return this->name; }
};
struct Hacker { // being a privately known shaddow-class/copy of 'Weak'
std::string name;
};
int main(int argc, char** argv) {
Weak w;
w.setName("Jon");
std::cout << w.getName() << std::endl;
Hacker *hackit = reinterpret_cast<Hacker *>(&w);
hackit->name = "Jack";
std::cout << w.getName() << std::endl;
} However, this generally is not considered good practice especially you hinted at encapsulation as a requirement (i.e. "not public the whole data"). The most popular, other, and cleaner approaches for (de-)deserialisation w/o accessing fields are, for example, to
Whatever your preference is, refl-cpp is versatile enough to support these and many other possible patterns. It all depends a bit on your application, API design style, which/how many protocols you want to support, and other requirements... |
@RalphSteinhagen Thanks for the detailed answer. Appreciated. For the hacking method, I'm not seeing a way to actually pull the full-fledged shadow struct declaration off of the header file, w/o indirections involving pointers like pimpl. If I can and possibly encapsulate that into a namespace provided that the padding and alignment are the same, I'm ok with this approach. Setters are really tedious and something I don't want to touch... My concrete problem has the program flow as follow,
Can you advise on how I should solve this? Thanks again. |
I guess you'll have always some code duplication with the constraints you mentioned: either registering and synchronising the getter/setter, or a method (that e.g. takes your text file line) to the private field layout, or synchronising a shadow to the public class definition. You could always put the shadow class (i.e. The choice depends a lot on how many classes and field-per-classes you have... hundreds of classes with few fields and/or few classes with hundreds of fields ... or something in between. For what it's worth: using compile-time reflection is IMHO still a better choice than relying on IDL-type serialiser that require a code-generator to instantiate the classes. You seem to hint at an object-oriented programming style... why not use (possibly immutable) plain-old data structs and a functional programming style? That makes things usually also easier in terms of concurrency and thread safety. Do you have a link to your project? |
This is what I'm currently have, either a manual contructor-ish method or a friend builder class that manually invoke the corresponding parsing function and assign to the fields of the concrete class accordingly. But with checked the correctness of the parsing result using std::expected, it becomes really tedious to duplicate all the codes. I have a few classes with each of them double digit data fields.
This part I don't quite get it. Can you elaborate more? I have posted the abstraction of my code in pseudo and sloppy style. Forgive my syntax. The create() method in ConcreteBuilder class is what I have for now and the place I want to refactor. compiler explorer link namespace parser {
enum class ParsingPattern {
kParseUint = 0,
// ...
}
template <class ReturnType, FieldFormat format, ParsingPattern Pattern>
requires(Pattern == ParsingPattern::kParseUint)
constexpr tl::expected<ReturnType, std::runtime_error> ParseField(std::string_view field_sv);
// other parsing pattern specialization
}
class Concrete {
public:
// some methods
private:
int field_a;
int64_t field_b
friend class ConcreteBuilder;
};
class ConcreteBuilder {
std::optional<Concrete> Create() {
Concrete c;
if (auto&& field_a_res = ParseField<decltype(field_a), {"%d"}, ParsingPattern::kParseInt>("42"); field_a_res.has_value() {
c.field_a = field_a_res.value();
} else {
LOG(ERROR) << field_a_res;
return std::nullopt;
}
auto&& field_b_part1_res = ParseField<field_b_part1_type, {"%d"}, ParsingPattern::kParseInt>("42");
auto&& field_b_part2_res = ParseField<field_b_part2_type, {"%d"}, ParsingPattern::kParseInt>("42");
if (!field_b_part1_res.has_value()) {
LOG(ERROR) << field_b_part1_res;
return std::nullopt;
} else if (!field_b_part2_res.has_value()) {
LOG(ERROR) << field_b_part2_res;
return std::nullopt;
} else {
c.field_b = field_b_reduction_op()(field_b_part1_res, field_b_part1_res);
}
// other fields
return c;
}
};
|
If you don't able to write to the struct, but you know the variable's name, constexpr time can be "get" private members/member functions by their pointer, see this repo |
A bit of newbie question I guess. With the current status of C++, are there any ways we can reflect on private member fields or methods?
My main use case is de-serialization and populate class private fields from a parser. What's the best approach if reflection is not possible? I'm willing to friend or expose to a particular builder but not public the whole data section. Thanks.
The text was updated successfully, but these errors were encountered: