Using 'override' for a function that has been declared using 'typedef'

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
15
down vote

favorite
1












C++ 11 introduced the 'override' specifier for functions and I find it useful as it makes it explicit that a virtual function is being overridden. However, I can't seem to get it work for a function that has been declared using a typedef.



I understand that 'override' is not a keyword, has it got something to do with that?



The following code illustrates my point:



#include <iostream>

typedef char ReturnsChar();

class Basic

public:
virtual char get_a();
virtual ReturnsChar get_z;
;

char Basic::get_a() return 'a';
char Basic::get_z() return 'z';

class Capitalized : public Basic

public:
// Can override explicitly if I use the normal definition
char get_a() override;

// Compiles if I use the typedef but not 'override'
ReturnsChar get_z;

// Will not compile, but would like to do this
//ReturnsChar get_z override;

;

char Capitalized::get_a() return 'A';
char Capitalized::get_z() return 'Z';

int main()

Basic foo;
Capitalized bar;

std::cout << foo.get_a() << std::endl; // a
std::cout << foo.get_z() << std::endl; // z
std::cout << bar.get_a() << std::endl; // A
std::cout << bar.get_z() << std::endl; // Z



I'm using GNU's g++ 8.2.0 and the error it gives me is




error: expected ';' at end of member declaration
ReturnsChar get_z override;
^~~~~
;
error: ‘override’ does not name a type; did you mean ‘ctermid’?
ReturnsChar get_z override;
^~~~~~~~
ctermid



EDIT: To address the comments, I understand this style is unclear. I am more interested about why this won't compile and what exactly 'override' does (especially because it is not a keyword).
As an aside, I feel like typedef-ing functions might be clear in some cases, say:



void (*foo(int x, void (*f)(int)))(int);


That's hard to read, especially if comes up often. I can just typedef that as 'UpdateAddressFunction' and then mentally think of every function of that type as something that 'updates an address'.










share|improve this question



















  • 9




    That is a really confusing / poor use of a typedef. That makes it very unclear within the class that get_z is a method and not a variable.
    – CoryKramer
    Aug 31 at 11:27






  • 3




    ^ completely agree on that. If you add the language-lawyer tag someone might tell if this is according to the standard or not, but I dont want to see a code review that accepts this :P
    – user463035818
    Aug 31 at 11:31






  • 1




    Yes, in this example I agree completely. However, in my actual use case there are some very complex function prototypes and function pointers that I use, which make typedef-ing of a function useful (I like to think). Either way, the aim of my question is more about understanding why it will not compile, as opposed to style.
    – Globe Theatre
    Aug 31 at 11:31






  • 1




    why not typedef the returntype only? isnt that sufficient?
    – user463035818
    Aug 31 at 11:32







  • 1




    @user463035818 Another bug. A virt-specifier isn't even allowed in the syntax for a regular declaration, only in a member-declaration within a class.
    – aschepler
    Aug 31 at 11:45














up vote
15
down vote

favorite
1












C++ 11 introduced the 'override' specifier for functions and I find it useful as it makes it explicit that a virtual function is being overridden. However, I can't seem to get it work for a function that has been declared using a typedef.



I understand that 'override' is not a keyword, has it got something to do with that?



The following code illustrates my point:



#include <iostream>

typedef char ReturnsChar();

class Basic

public:
virtual char get_a();
virtual ReturnsChar get_z;
;

char Basic::get_a() return 'a';
char Basic::get_z() return 'z';

class Capitalized : public Basic

public:
// Can override explicitly if I use the normal definition
char get_a() override;

// Compiles if I use the typedef but not 'override'
ReturnsChar get_z;

// Will not compile, but would like to do this
//ReturnsChar get_z override;

;

char Capitalized::get_a() return 'A';
char Capitalized::get_z() return 'Z';

int main()

Basic foo;
Capitalized bar;

std::cout << foo.get_a() << std::endl; // a
std::cout << foo.get_z() << std::endl; // z
std::cout << bar.get_a() << std::endl; // A
std::cout << bar.get_z() << std::endl; // Z



I'm using GNU's g++ 8.2.0 and the error it gives me is




error: expected ';' at end of member declaration
ReturnsChar get_z override;
^~~~~
;
error: ‘override’ does not name a type; did you mean ‘ctermid’?
ReturnsChar get_z override;
^~~~~~~~
ctermid



EDIT: To address the comments, I understand this style is unclear. I am more interested about why this won't compile and what exactly 'override' does (especially because it is not a keyword).
As an aside, I feel like typedef-ing functions might be clear in some cases, say:



void (*foo(int x, void (*f)(int)))(int);


That's hard to read, especially if comes up often. I can just typedef that as 'UpdateAddressFunction' and then mentally think of every function of that type as something that 'updates an address'.










share|improve this question



















  • 9




    That is a really confusing / poor use of a typedef. That makes it very unclear within the class that get_z is a method and not a variable.
    – CoryKramer
    Aug 31 at 11:27






  • 3




    ^ completely agree on that. If you add the language-lawyer tag someone might tell if this is according to the standard or not, but I dont want to see a code review that accepts this :P
    – user463035818
    Aug 31 at 11:31






  • 1




    Yes, in this example I agree completely. However, in my actual use case there are some very complex function prototypes and function pointers that I use, which make typedef-ing of a function useful (I like to think). Either way, the aim of my question is more about understanding why it will not compile, as opposed to style.
    – Globe Theatre
    Aug 31 at 11:31






  • 1




    why not typedef the returntype only? isnt that sufficient?
    – user463035818
    Aug 31 at 11:32







  • 1




    @user463035818 Another bug. A virt-specifier isn't even allowed in the syntax for a regular declaration, only in a member-declaration within a class.
    – aschepler
    Aug 31 at 11:45












up vote
15
down vote

favorite
1









up vote
15
down vote

favorite
1






1





C++ 11 introduced the 'override' specifier for functions and I find it useful as it makes it explicit that a virtual function is being overridden. However, I can't seem to get it work for a function that has been declared using a typedef.



I understand that 'override' is not a keyword, has it got something to do with that?



The following code illustrates my point:



#include <iostream>

typedef char ReturnsChar();

class Basic

public:
virtual char get_a();
virtual ReturnsChar get_z;
;

char Basic::get_a() return 'a';
char Basic::get_z() return 'z';

class Capitalized : public Basic

public:
// Can override explicitly if I use the normal definition
char get_a() override;

// Compiles if I use the typedef but not 'override'
ReturnsChar get_z;

// Will not compile, but would like to do this
//ReturnsChar get_z override;

;

char Capitalized::get_a() return 'A';
char Capitalized::get_z() return 'Z';

int main()

Basic foo;
Capitalized bar;

std::cout << foo.get_a() << std::endl; // a
std::cout << foo.get_z() << std::endl; // z
std::cout << bar.get_a() << std::endl; // A
std::cout << bar.get_z() << std::endl; // Z



I'm using GNU's g++ 8.2.0 and the error it gives me is




error: expected ';' at end of member declaration
ReturnsChar get_z override;
^~~~~
;
error: ‘override’ does not name a type; did you mean ‘ctermid’?
ReturnsChar get_z override;
^~~~~~~~
ctermid



EDIT: To address the comments, I understand this style is unclear. I am more interested about why this won't compile and what exactly 'override' does (especially because it is not a keyword).
As an aside, I feel like typedef-ing functions might be clear in some cases, say:



void (*foo(int x, void (*f)(int)))(int);


That's hard to read, especially if comes up often. I can just typedef that as 'UpdateAddressFunction' and then mentally think of every function of that type as something that 'updates an address'.










share|improve this question















C++ 11 introduced the 'override' specifier for functions and I find it useful as it makes it explicit that a virtual function is being overridden. However, I can't seem to get it work for a function that has been declared using a typedef.



I understand that 'override' is not a keyword, has it got something to do with that?



The following code illustrates my point:



#include <iostream>

typedef char ReturnsChar();

class Basic

public:
virtual char get_a();
virtual ReturnsChar get_z;
;

char Basic::get_a() return 'a';
char Basic::get_z() return 'z';

class Capitalized : public Basic

public:
// Can override explicitly if I use the normal definition
char get_a() override;

// Compiles if I use the typedef but not 'override'
ReturnsChar get_z;

// Will not compile, but would like to do this
//ReturnsChar get_z override;

;

char Capitalized::get_a() return 'A';
char Capitalized::get_z() return 'Z';

int main()

Basic foo;
Capitalized bar;

std::cout << foo.get_a() << std::endl; // a
std::cout << foo.get_z() << std::endl; // z
std::cout << bar.get_a() << std::endl; // A
std::cout << bar.get_z() << std::endl; // Z



I'm using GNU's g++ 8.2.0 and the error it gives me is




error: expected ';' at end of member declaration
ReturnsChar get_z override;
^~~~~
;
error: ‘override’ does not name a type; did you mean ‘ctermid’?
ReturnsChar get_z override;
^~~~~~~~
ctermid



EDIT: To address the comments, I understand this style is unclear. I am more interested about why this won't compile and what exactly 'override' does (especially because it is not a keyword).
As an aside, I feel like typedef-ing functions might be clear in some cases, say:



void (*foo(int x, void (*f)(int)))(int);


That's hard to read, especially if comes up often. I can just typedef that as 'UpdateAddressFunction' and then mentally think of every function of that type as something that 'updates an address'.







c++ c++11 c++14 language-lawyer






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Aug 31 at 16:40

























asked Aug 31 at 11:24









Globe Theatre

23018




23018







  • 9




    That is a really confusing / poor use of a typedef. That makes it very unclear within the class that get_z is a method and not a variable.
    – CoryKramer
    Aug 31 at 11:27






  • 3




    ^ completely agree on that. If you add the language-lawyer tag someone might tell if this is according to the standard or not, but I dont want to see a code review that accepts this :P
    – user463035818
    Aug 31 at 11:31






  • 1




    Yes, in this example I agree completely. However, in my actual use case there are some very complex function prototypes and function pointers that I use, which make typedef-ing of a function useful (I like to think). Either way, the aim of my question is more about understanding why it will not compile, as opposed to style.
    – Globe Theatre
    Aug 31 at 11:31






  • 1




    why not typedef the returntype only? isnt that sufficient?
    – user463035818
    Aug 31 at 11:32







  • 1




    @user463035818 Another bug. A virt-specifier isn't even allowed in the syntax for a regular declaration, only in a member-declaration within a class.
    – aschepler
    Aug 31 at 11:45












  • 9




    That is a really confusing / poor use of a typedef. That makes it very unclear within the class that get_z is a method and not a variable.
    – CoryKramer
    Aug 31 at 11:27






  • 3




    ^ completely agree on that. If you add the language-lawyer tag someone might tell if this is according to the standard or not, but I dont want to see a code review that accepts this :P
    – user463035818
    Aug 31 at 11:31






  • 1




    Yes, in this example I agree completely. However, in my actual use case there are some very complex function prototypes and function pointers that I use, which make typedef-ing of a function useful (I like to think). Either way, the aim of my question is more about understanding why it will not compile, as opposed to style.
    – Globe Theatre
    Aug 31 at 11:31






  • 1




    why not typedef the returntype only? isnt that sufficient?
    – user463035818
    Aug 31 at 11:32







  • 1




    @user463035818 Another bug. A virt-specifier isn't even allowed in the syntax for a regular declaration, only in a member-declaration within a class.
    – aschepler
    Aug 31 at 11:45







9




9




That is a really confusing / poor use of a typedef. That makes it very unclear within the class that get_z is a method and not a variable.
– CoryKramer
Aug 31 at 11:27




That is a really confusing / poor use of a typedef. That makes it very unclear within the class that get_z is a method and not a variable.
– CoryKramer
Aug 31 at 11:27




3




3




^ completely agree on that. If you add the language-lawyer tag someone might tell if this is according to the standard or not, but I dont want to see a code review that accepts this :P
– user463035818
Aug 31 at 11:31




^ completely agree on that. If you add the language-lawyer tag someone might tell if this is according to the standard or not, but I dont want to see a code review that accepts this :P
– user463035818
Aug 31 at 11:31




1




1




Yes, in this example I agree completely. However, in my actual use case there are some very complex function prototypes and function pointers that I use, which make typedef-ing of a function useful (I like to think). Either way, the aim of my question is more about understanding why it will not compile, as opposed to style.
– Globe Theatre
Aug 31 at 11:31




Yes, in this example I agree completely. However, in my actual use case there are some very complex function prototypes and function pointers that I use, which make typedef-ing of a function useful (I like to think). Either way, the aim of my question is more about understanding why it will not compile, as opposed to style.
– Globe Theatre
Aug 31 at 11:31




1




1




why not typedef the returntype only? isnt that sufficient?
– user463035818
Aug 31 at 11:32





why not typedef the returntype only? isnt that sufficient?
– user463035818
Aug 31 at 11:32





1




1




@user463035818 Another bug. A virt-specifier isn't even allowed in the syntax for a regular declaration, only in a member-declaration within a class.
– aschepler
Aug 31 at 11:45




@user463035818 Another bug. A virt-specifier isn't even allowed in the syntax for a regular declaration, only in a member-declaration within a class.
– aschepler
Aug 31 at 11:45












1 Answer
1






active

oldest

votes

















up vote
23
down vote



accepted











I understand that 'override' is not a keyword, has it got something to do with that?




It does: override is only recognised in a few specific contexts and is available as a name for ordinary use otherwise. However, I believe this is a compiler bug, and this is one context where the compiler should recognise it in its special meaning.



The relevant grammatical production for this is




[class.mem]



member-declarator:

  declarator virt-specifier-seqopt pure-specifieropt




This does not require that a virt-specifier-seq only appear in a declaration containing parameters. The only similar requirement is stated below:




[class.mem]p8:



A virt-specifier-seq shall appear only in the declaration of a virtual member function (10.3).




In your case, the declaration is one of a virtual member function. I believe override should be accepted here.






share|improve this answer




















  • The one catch is [lex.name]/2 "Unless otherwise specified, any ambiguity as to whether a given identifier has a special meaning is resolved to interpret the token as a regular identifier." I don't see a way that code could be ambiguous, but I'm not 100% certain.
    – aschepler
    Aug 31 at 11:39










  • Thanks, this is very clear. I'm quite pleased that I've discovered an actual compiler bug, that's one to tick off the bucket list!
    – Globe Theatre
    Aug 31 at 11:45










  • @aschepler Good point. I think there are some similar cases with ambiguities. I think struct A struct B ; struct B override; ; might be one, which because of what you quoted declares a non-static data member named override.
    – hvd
    Aug 31 at 11:45











  • Did you already file some bug reports?
    – Rakete1111
    Aug 31 at 15:41






  • 2




    @hvd Done: gcc.gnu.org/bugzilla/show_bug.cgi?id=87174
    – Rakete1111
    Aug 31 at 18:07










Your Answer





StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52114447%2fusing-override-for-a-function-that-has-been-declared-using-typedef%23new-answer', 'question_page');

);

Post as a guest






























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
23
down vote



accepted











I understand that 'override' is not a keyword, has it got something to do with that?




It does: override is only recognised in a few specific contexts and is available as a name for ordinary use otherwise. However, I believe this is a compiler bug, and this is one context where the compiler should recognise it in its special meaning.



The relevant grammatical production for this is




[class.mem]



member-declarator:

  declarator virt-specifier-seqopt pure-specifieropt




This does not require that a virt-specifier-seq only appear in a declaration containing parameters. The only similar requirement is stated below:




[class.mem]p8:



A virt-specifier-seq shall appear only in the declaration of a virtual member function (10.3).




In your case, the declaration is one of a virtual member function. I believe override should be accepted here.






share|improve this answer




















  • The one catch is [lex.name]/2 "Unless otherwise specified, any ambiguity as to whether a given identifier has a special meaning is resolved to interpret the token as a regular identifier." I don't see a way that code could be ambiguous, but I'm not 100% certain.
    – aschepler
    Aug 31 at 11:39










  • Thanks, this is very clear. I'm quite pleased that I've discovered an actual compiler bug, that's one to tick off the bucket list!
    – Globe Theatre
    Aug 31 at 11:45










  • @aschepler Good point. I think there are some similar cases with ambiguities. I think struct A struct B ; struct B override; ; might be one, which because of what you quoted declares a non-static data member named override.
    – hvd
    Aug 31 at 11:45











  • Did you already file some bug reports?
    – Rakete1111
    Aug 31 at 15:41






  • 2




    @hvd Done: gcc.gnu.org/bugzilla/show_bug.cgi?id=87174
    – Rakete1111
    Aug 31 at 18:07














up vote
23
down vote



accepted











I understand that 'override' is not a keyword, has it got something to do with that?




It does: override is only recognised in a few specific contexts and is available as a name for ordinary use otherwise. However, I believe this is a compiler bug, and this is one context where the compiler should recognise it in its special meaning.



The relevant grammatical production for this is




[class.mem]



member-declarator:

  declarator virt-specifier-seqopt pure-specifieropt




This does not require that a virt-specifier-seq only appear in a declaration containing parameters. The only similar requirement is stated below:




[class.mem]p8:



A virt-specifier-seq shall appear only in the declaration of a virtual member function (10.3).




In your case, the declaration is one of a virtual member function. I believe override should be accepted here.






share|improve this answer




















  • The one catch is [lex.name]/2 "Unless otherwise specified, any ambiguity as to whether a given identifier has a special meaning is resolved to interpret the token as a regular identifier." I don't see a way that code could be ambiguous, but I'm not 100% certain.
    – aschepler
    Aug 31 at 11:39










  • Thanks, this is very clear. I'm quite pleased that I've discovered an actual compiler bug, that's one to tick off the bucket list!
    – Globe Theatre
    Aug 31 at 11:45










  • @aschepler Good point. I think there are some similar cases with ambiguities. I think struct A struct B ; struct B override; ; might be one, which because of what you quoted declares a non-static data member named override.
    – hvd
    Aug 31 at 11:45











  • Did you already file some bug reports?
    – Rakete1111
    Aug 31 at 15:41






  • 2




    @hvd Done: gcc.gnu.org/bugzilla/show_bug.cgi?id=87174
    – Rakete1111
    Aug 31 at 18:07












up vote
23
down vote



accepted







up vote
23
down vote



accepted







I understand that 'override' is not a keyword, has it got something to do with that?




It does: override is only recognised in a few specific contexts and is available as a name for ordinary use otherwise. However, I believe this is a compiler bug, and this is one context where the compiler should recognise it in its special meaning.



The relevant grammatical production for this is




[class.mem]



member-declarator:

  declarator virt-specifier-seqopt pure-specifieropt




This does not require that a virt-specifier-seq only appear in a declaration containing parameters. The only similar requirement is stated below:




[class.mem]p8:



A virt-specifier-seq shall appear only in the declaration of a virtual member function (10.3).




In your case, the declaration is one of a virtual member function. I believe override should be accepted here.






share|improve this answer













I understand that 'override' is not a keyword, has it got something to do with that?




It does: override is only recognised in a few specific contexts and is available as a name for ordinary use otherwise. However, I believe this is a compiler bug, and this is one context where the compiler should recognise it in its special meaning.



The relevant grammatical production for this is




[class.mem]



member-declarator:

  declarator virt-specifier-seqopt pure-specifieropt




This does not require that a virt-specifier-seq only appear in a declaration containing parameters. The only similar requirement is stated below:




[class.mem]p8:



A virt-specifier-seq shall appear only in the declaration of a virtual member function (10.3).




In your case, the declaration is one of a virtual member function. I believe override should be accepted here.







share|improve this answer












share|improve this answer



share|improve this answer










answered Aug 31 at 11:36









hvd

113k11194273




113k11194273











  • The one catch is [lex.name]/2 "Unless otherwise specified, any ambiguity as to whether a given identifier has a special meaning is resolved to interpret the token as a regular identifier." I don't see a way that code could be ambiguous, but I'm not 100% certain.
    – aschepler
    Aug 31 at 11:39










  • Thanks, this is very clear. I'm quite pleased that I've discovered an actual compiler bug, that's one to tick off the bucket list!
    – Globe Theatre
    Aug 31 at 11:45










  • @aschepler Good point. I think there are some similar cases with ambiguities. I think struct A struct B ; struct B override; ; might be one, which because of what you quoted declares a non-static data member named override.
    – hvd
    Aug 31 at 11:45











  • Did you already file some bug reports?
    – Rakete1111
    Aug 31 at 15:41






  • 2




    @hvd Done: gcc.gnu.org/bugzilla/show_bug.cgi?id=87174
    – Rakete1111
    Aug 31 at 18:07
















  • The one catch is [lex.name]/2 "Unless otherwise specified, any ambiguity as to whether a given identifier has a special meaning is resolved to interpret the token as a regular identifier." I don't see a way that code could be ambiguous, but I'm not 100% certain.
    – aschepler
    Aug 31 at 11:39










  • Thanks, this is very clear. I'm quite pleased that I've discovered an actual compiler bug, that's one to tick off the bucket list!
    – Globe Theatre
    Aug 31 at 11:45










  • @aschepler Good point. I think there are some similar cases with ambiguities. I think struct A struct B ; struct B override; ; might be one, which because of what you quoted declares a non-static data member named override.
    – hvd
    Aug 31 at 11:45











  • Did you already file some bug reports?
    – Rakete1111
    Aug 31 at 15:41






  • 2




    @hvd Done: gcc.gnu.org/bugzilla/show_bug.cgi?id=87174
    – Rakete1111
    Aug 31 at 18:07















The one catch is [lex.name]/2 "Unless otherwise specified, any ambiguity as to whether a given identifier has a special meaning is resolved to interpret the token as a regular identifier." I don't see a way that code could be ambiguous, but I'm not 100% certain.
– aschepler
Aug 31 at 11:39




The one catch is [lex.name]/2 "Unless otherwise specified, any ambiguity as to whether a given identifier has a special meaning is resolved to interpret the token as a regular identifier." I don't see a way that code could be ambiguous, but I'm not 100% certain.
– aschepler
Aug 31 at 11:39












Thanks, this is very clear. I'm quite pleased that I've discovered an actual compiler bug, that's one to tick off the bucket list!
– Globe Theatre
Aug 31 at 11:45




Thanks, this is very clear. I'm quite pleased that I've discovered an actual compiler bug, that's one to tick off the bucket list!
– Globe Theatre
Aug 31 at 11:45












@aschepler Good point. I think there are some similar cases with ambiguities. I think struct A struct B ; struct B override; ; might be one, which because of what you quoted declares a non-static data member named override.
– hvd
Aug 31 at 11:45





@aschepler Good point. I think there are some similar cases with ambiguities. I think struct A struct B ; struct B override; ; might be one, which because of what you quoted declares a non-static data member named override.
– hvd
Aug 31 at 11:45













Did you already file some bug reports?
– Rakete1111
Aug 31 at 15:41




Did you already file some bug reports?
– Rakete1111
Aug 31 at 15:41




2




2




@hvd Done: gcc.gnu.org/bugzilla/show_bug.cgi?id=87174
– Rakete1111
Aug 31 at 18:07




@hvd Done: gcc.gnu.org/bugzilla/show_bug.cgi?id=87174
– Rakete1111
Aug 31 at 18:07

















 

draft saved


draft discarded















































 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52114447%2fusing-override-for-a-function-that-has-been-declared-using-typedef%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

How to check contact read email or not when send email to Individual?

Bahrain

Postfix configuration issue with fips on centos 7; mailgun relay