Reputation: 1954
I have the following code to provide a factory to create an encoder/decoder for a given data type and encoding scheme (BITPACK, PLAIN, etc), and the code works.
class Encoding {
public:
virtual std::unique_ptr<Encoder> encoder() = 0;
virtual std::unique_ptr<Decoder> decoder() = 0;
};
template <class E, class D>
class EncodingTemplate : public Encoding {
public:
std::unique_ptr<Encoder> encoder() override {
return std::unique_ptr<Encoder>(new E());
}
std::unique_ptr<Decoder> decoder() override {
return std::unique_ptr<Decoder>(new D());
}
};
namespace u32 {
class PlainEncoder {...};
class PlainDecoder {...};
class BitpackEncoder {...};
class BitpackDecoder {...};
EncodingTemplate<PlainEncoder, PlainDecoder> plainEncoding;
EncodingTemplate<BitpackEncoder, BitpackDecoder> bitpackEncoding;
Encoding& EncodingFactory::Get(EncodingType encoding) {
switch (encoding) {
case PLAIN:
return plainEncoding;
case BITPACK:
return bitpackEncoding;
default:
return plainEncoding;
}
}
} // namespace u32
// This is where we invoke the encoder
class BlockBuilder {
void Build() {
....
std::unique_ptr<Encoder> key_encoder = u32::EncodingFactory::Get(BITPACK).encoder();
};
Now I want to modify the factory to create a shared encoder/decoder and reuse it. So I add a constructor to EncodingTemplate
(This is the first step, and it's just an empty constructor.) Then the code stopped working and AddressSanitizer complains about seg fault.
SEGV on unknown address 0x000000000000 (pc 0x55ca3c817eec bp 0x7fffe7bdd190 sp 0x7fffe7bdd090 T0)
BlockBuilder::Build(): block_builder.cc:16
BlockBuilder::Add(leveldb::Slice const&, leveldb::Slice const&)
BlockReadBenchmark::BlockReadBenchmark()
BlockReadBenchmark_Normal_Benchmark::BlockReadBenchmark_Normal_Benchmark()
__static_initialization_and_destruction_0
_GLOBAL__sub_I__Z13binary_sorterii
__libc_csu_init
__libc_start_main
_start
Here's my new EncodingTemplate
, it only adds an empty constructor.
class EncodingTemplate : public Encoding {
public:
EncodingTemplate() {} // Only add an empty constructor here
std::unique_ptr<Encoder> encoder() override {
return std::unique_ptr<Encoder>(new E());
}
std::unique_ptr<Decoder> decoder() override {
return std::unique_ptr<Decoder>(new D());
}
};
A weird thing is, I can reproduce the error in my project. I comment out the constructor and the code works. I uncomment it, and the error comes back. But when I try to create a minimized working example, I failed to reproduce it.
Can anyone help me figure out what is happening? I really have no idea why a reference to a global variable could cause SEG FAULT. And what does a constructor have to do with it.
Thanks!
Upvotes: 0
Views: 76
Reputation: 30807
The typical solution to this is the Singleton pattern, since that defers initialization to the first time the object is needed. Since you have a factory already, you can use a function-local static
variable:
Encoding& EncodingFactory::Get(EncodingType encoding) {
static EncodingTemplate<PlainEncoder, PlainDecoder> plainEncoding;
static EncodingTemplate<BitpackEncoder, BitpackDecoder> bitpackEncoding;
switch (encoding) {
case PLAIN:
return plainEncoding;
case BITPACK:
return bitpackEncoding;
default:
return plainEncoding;
}
}
Upvotes: 2