Showing posts with label gcc. Show all posts
Showing posts with label gcc. Show all posts

Thursday, December 28, 2023

Narrowing type conversions in C/C++

 Hi, my friends and just everyone! :)

You shouldn't think that I forgot about my blog. After more then 6 years I decided to continue writing articles. And I'm glad to present my new article about strange behavior of C++ compilers. However, the reason maybe more interesting. E.g. in C++ standards.

So today I would like to talk about implicit type conversions and particular about implicit narrowing conversion of types.

Me and my friends were sure that if we can lose data the compiler has to show error or at least show warning.
Actually, look at this:

1
2
3
4
5
6
7
8
#include <cstdint>

void f(uint16_t) {}

int main() {
    uint32_t x = 0xFFFFFFFF;
    f(x);
}

I think you are sure that gcc or clang compilers have to show warning or error? :) But no!
$ rm -f a.out; g++ main1.cpp && ./a.out
$ rm -f a.out; clang++ main1.cpp && ./a.out
$
As we can see there are no any warnings or errors. Hmmm. Ok! Maybe we can use special warnings level? No problem!
$ rm -f a.out; g++ -Wall -Wextra -Wpedantic main1.cpp && ./a.out
$ rm -f a.out; clang++ -Wall -Wextra -Wpedantic main1.cpp && ./a.out
$
So, we don't see any warnings or errors. And any warnings flags couldn't help. In that case we can write the program which can demonstrate big problems with that behavior.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <cstdint>

void Bob(uint16_t x) {
    std::cout << "I'm Bob. My salary is $"
              << x
              << std::endl;
}

struct Snob {
    Snob(uint16_t x) {
        std::cout << "I'm Snob and my salary is $"
                  << x
                  << std::endl;
    }
};

int main() {
    uint32_t x = 0xFFFF0001; // 4294901761

    std::cout << "I'm the boss and I would like to give money to Bob and Snob. "
              << "Guys! Take your salary! "
              << "It is $"
              << x
              << std::endl;
    
    Bob(x);

    Snob snob(x);
}

The result (gcc):
$ rm -f a.out; g++ -std=c++17 -Wall -Wextra -Wpedantic main2.cpp && ./a.out
I'm the boss and I would like to give money to Bob and Snob. Guys! Take your salary!
It is $4294901761
I'm Bob. My salary is $1
I'm Snob and my salary is $1
$
The result (clang):
$ rm -f a.out; clang++ -std=c++17 -Wall -Wextra -Wpedantic main2.cpp && ./a.out
I'm the boss and I would like to give money to Bob and Snob. Guys! Take your salary!
It is $4294901761
I'm Bob. My salary is $1
I'm Snob and my salary is $1
$

OK? Do you understand what problems can be? Currently, I have no idea about reasons. I have not found the explanation of this behavior yet. So if you know I would be happy if you could explain to me.

Have a nice time! :)
Best regards,
Vasiliy V. Bodrov aka Bodro,
the 28-th of December, 2023.

Tuesday, May 16, 2017

GNU Compiler Collection: extensions, nested functions, object-oriented programming, functional paradigm etc. It's the C language only!

Hello my dear frineds!

I want to tell you about possibilities of the C language. I will do it with the example of a linear list.

You can see the links:
All GCC Extensions
Nested Functions

So. It's a structure of the linked list:
    struct storage_data_s {
        value_t                value;
        char*                  name;
        char*                  comment;
        struct storage_data_s* next;
    }* data;

It's a structure of the storage with the linked list:
/* It's a storage */
typedef struct storage_s {
    struct storage_data_s {
        value_t                value;
        char*                  name;
        char*                  comment;
        struct storage_data_s* next;
    }* data;

    void (*init)(struct storage_s* _this);
    
    void (*add)(struct storage_s* _this,
                value_t _value,
                char* _name,
                char* _comment);
    
    void (*for_each)(struct storage_s const* _this,
                     output_f output);
    
    void (*clear)(struct storage_s* _this);
    
    void (*deinit)(struct storage_s* _this);
}      storage_t,
      *storage_p,
     **storage_pp,
*const storage_cp;

typedef storage_t const* storage_pc;
typedef storage_t const* const storage_cpc;

The first argument of all functions is a pointer to "this" (self, this etc).

This is the complete text of the code (I apologize for the strange syntax highlighting):

storage.h:
/* ****************************************************************************
 * Programmer: Vasiliy V. Bodrov aka Bodro
 * May 15, 2016.
 * ****************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Vasiliy V. Bodrov aka Bodro
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
 * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 **************************************************************************** */

#pragma once

#include <stdbool.h>

/* It's a type of value */
typedef void* value_t;
typedef void** value_p;
typedef void const* value_tc;

/* The user must to determine these functions */
#ifndef __STORAGE_C_FILE__
typedef struct __value_user_helpers_handlers_s {
    value_t (*new)(user_value_t _uv);
    user_value_t (*get)(value_tc _v);
    user_value_t (*get2)(value_tc _v, user_value_t _container);
}  __value_user_helpers_handlers_t,
  *__value_user_helpers_handlers_p;
#else /* __STORAGE_C_FILE__ */
typedef void*  __value_user_helpers_handlers_p;
#endif /* __STORAGE_C_FILE__ */

typedef struct value_handlers_s {
    void (*delete)(value_p _v);
    value_t (*copy)(value_tc _v);
    value_t (*move)(value_p _v);
    bool (*p_move)(void); /* predicate */
    
    __value_user_helpers_handlers_p x;
} value_handlers_t;

#ifdef __STORAGE_C_FILE__
value_handlers_t __value_helpers_handlers;
#else /* __STORAGE_C_FILE__ */
extern value_handlers_t __value_helpers_handlers;
#endif /* __STORAGE_C_FILE__ */

/* This type is pointer to output function */
typedef void (*output_f)(value_t _value,
                         char const* _name,
                         char const* _comment);

/* It's a storage */
typedef struct storage_s {
    struct storage_data_s {
        value_t                value;
        char*                  name;
        char*                  comment;
        struct storage_data_s* next;
    }* data;

    void (*init)(struct storage_s* _this);
    
    void (*add)(struct storage_s* _this,
                value_t _value,
                char* _name,
                char* _comment);
    
    void (*for_each)(struct storage_s const* _this,
                     output_f output);
    
    void (*clear)(struct storage_s* _this);
    
    void (*deinit)(struct storage_s* _this);
}      storage_t,
      *storage_p,
     **storage_pp,
*const storage_cp;

typedef storage_t const* storage_pc;
typedef storage_t const* const storage_cpc;

/* These are creator and remover */
storage_p storage_creator(void);
void storage_remover(storage_pp);

/* ****************************************************************************
 * End of file
 **************************************************************************** */



storage.c:
/* ****************************************************************************
 * Programmer: Vasiliy V. Bodrov aka Bodro
 * May 15, 2016.
 * ****************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Vasiliy V. Bodrov aka Bodro
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
 * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 **************************************************************************** */

#define __STORAGE_C_FILE__

#include "storage.h"

#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdbool.h>

static void handler_init(struct storage_s*);
static void handler_add(struct storage_s*, value_t, char*, char*);
static void handler_for_each(struct storage_s const*, output_f);
static void handler_clear(struct storage_s*);
static void handler_deinit(struct storage_s*);

/* 
 * These are exported functions
 */
storage_p storage_creator(void) {
    storage_p _storage = NULL;
    
    _storage = (storage_p) calloc(1, sizeof(storage_t));
    assert(_storage);

    if(_storage) {
        _storage->init     = handler_init;
        _storage->add      = handler_add;
        _storage->for_each = handler_for_each;
        _storage->clear    = handler_clear;
        _storage->deinit   = handler_deinit;

        _storage->init(_storage);
    }
            
    return _storage;
}

void storage_remover(storage_pp _storage) {
    assert(*_storage);

    if(*_storage) {
        if((*_storage)->deinit) {
            (*_storage)->deinit(*_storage);
        }

        free(*_storage);
        
        *_storage = NULL;
    }
}

/* 
 * These are static (internal) functions
 */

void handler_init(struct storage_s* _this) {
    assert(_this);
    
    _this->clear(_this);
    _this->data = NULL;
}

void handler_add(struct storage_s* _this,
                 value_t _value, char* _name, char* _comment) {
    assert(_this);

    auto struct storage_data_s* _search_end(struct storage_data_s*);
    auto struct storage_data_s* _allocator(struct storage_data_s*);
    auto void _add(struct storage_data_s*);
    auto value_t _copy(value_p _value);

    _add(_allocator(_search_end(_this->data)));

    struct storage_data_s* _search_end(struct storage_data_s* _) {
        if(!_ || !_->next) return _;
        return _search_end(_->next);
    }

    struct storage_data_s* _allocator(struct storage_data_s* _) {
        struct storage_data_s* _new =
            (struct storage_data_s*) calloc(1, sizeof(struct storage_data_s));
        assert(_new);

        if(_) _->next = _new;
        else _this->data = _new;

        return _new;
    }

    void _add(struct storage_data_s* _) {
        assert(_);
        
        _->value   = _copy(&_value);
        _->name    = (_name) ? strdup(_name) : NULL;
        _->comment = (_comment) ? strdup(_comment) : NULL;
        _->next    = NULL;
    }
    
    value_t _copy(value_p _value) {
        return (__value_helpers_handlers.p_move()) ?
            __value_helpers_handlers.move(_value) :
            __value_helpers_handlers.copy(*_value);
    }
}

void handler_for_each(struct storage_s const* _this,
                      output_f _output)
{
    assert(_this);
    assert(_output);

    auto void _f(struct storage_data_s*);

    _f(_this->data);

    void _f(struct storage_data_s* _) {
        if(!_) return;
        _output(_->value, _->name, _->comment);
        _f(_->next);
    }
}

void handler_clear(struct storage_s* _this)
{
    assert(_this);

    auto void _purge(struct storage_data_s*);

    _purge(_this->data);

    _this->data = NULL;

    void _purge(struct storage_data_s* _)
    {
        if(!_) return;

        if(_->name) {
            free(_->name);
        }

        if(_->comment) {
            free(_->comment);
        }

        __value_helpers_handlers.delete(&(_->value));
        _->value   = NULL;
        _->name    = NULL;
        _->comment = NULL;

        /* Note. It isn't a tail call! I can do the tail recursion,
         * but I don't want this. So I use direct recursion. Bodro.
         */
        _purge(_->next);

        _->next = NULL;
        free(_);
        _ = NULL;
    }
}

void handler_deinit(struct storage_s* _this)
{
    assert(_this);
    
    _this->clear(_this);
    _this->data = NULL;
}

#ifdef __STORAGE_C_FILE__
#undef __STORAGE_C_FILE__
#endif /* __STORAGE_C_FILE__ */

/* ****************************************************************************
 * End of file
 **************************************************************************** */



test.c:
/* ****************************************************************************
 * Programmer: Vasiliy V. Bodrov aka Bodro
 * May 15, 2016.
 * ****************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Vasiliy V. Bodrov aka Bodro
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
 * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 **************************************************************************** */

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

/* BEGIN >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
/* We prepare the user types and functions */
struct string_s;

typedef struct string_func_s {
    char* (*s_new)(struct string_s* _this, char const* _s);
    char const* (*s_get)(struct string_s* _this);
    void (*s_del)(struct string_s* _this);
} string_func_t;

typedef struct string_s {
    struct string_s* _self;     /* It's a pointer to yourself */
    char* s;                    /* It's a date (string) */
    struct string_func_s* f;    /* It's a pointer to table of functions */
} string;

typedef string user_value_t;

#include "storage.h"
/* END <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */

string new_string(string_func_t* _f);
string new_string2(string_func_t* _f, char const* _v);
void del_string(string* _s);

inline static char* handler_s_new(struct string_s*, char const*);
inline static char const* handler_s_get(struct string_s*);
inline static void handler_s_del(struct string_s*);

void user_handler_delete(value_p);
value_t user_handler_copy(value_tc);
value_t user_handler_move(value_p);
value_t user_handler_new(user_value_t);
user_value_t user_handler_get(value_tc);
user_value_t user_handler_get2(value_tc, user_value_t);

string new_string(string_func_t* _f) {
    string* _s = malloc(sizeof(string));
    
    assert(_s);

    _s->_self = _s;
    _s->s = NULL;
    _s->f = _f;

    return *_s;
}

string new_string2(string_func_t* _f, char const* _v) {
    string s = new_string(_f);
    s.f->s_new(s._self, _v);
    return s;
}

void del_string(string* _s) {
    if(_s) {
        _s->f->s_del(_s);
        _s->f = NULL;
        _s->_self = NULL;
        free(_s);
    }
}

char* handler_s_new(struct string_s* _this, char const* _s) {
    assert(_this);
    if(_this && _this->s) _this->f->s_del(_this);
    _this->s = (_s) ? strdup(_s) : NULL;
    return _this->s;
}

char const* handler_s_get(struct string_s* _this) {
    assert(_this);
    return _this->s;
}

void handler_s_del(struct string_s* _this) {
    assert(_this);
    if(_this && _this->s) {
        free(_this->s);
        _this->s = NULL;
    }
}

void user_handler_delete(value_p _v) {
    if(_v && *_v) {
        del_string((string*) *_v);
        *_v = NULL;
    }
}

value_t user_handler_copy(value_tc _v) {
    string s_old = user_handler_get(_v);
    string s_new = new_string2(s_old.f, s_old.f->s_get(s_old._self));
    return user_handler_new(s_new);
}

value_t user_handler_move(value_p _v) {
    assert(_v);
    string s_old = user_handler_get(*_v);
    string s_new = new_string2(s_old.f, s_old.f->s_get(s_old._self));
    del_string(s_old._self);
    
    return user_handler_new(s_new);
}

value_t user_handler_new(user_value_t _uv) {
    return (value_t) _uv._self;
}

user_value_t user_handler_get(value_tc _v) {
    assert(_v);
    return *((string*) _v);
}

user_value_t user_handler_get2(value_tc _v, user_value_t _container) {
    (void) _container;
    return user_handler_get(_v);
}

int main(int argc, char** argv) {
    (void) argc; (void) argv;

    auto bool p_true(void);
    auto bool p_false(void);
    
    string_func_t s_func;
    
    s_func.s_new = handler_s_new;
    s_func.s_get = handler_s_get;
    s_func.s_del = handler_s_del;

    __value_user_helpers_handlers_t user_helpers;

    user_helpers.new    = user_handler_new;
    user_helpers.get    = user_handler_get;
    user_helpers.get2   = user_handler_get2;
    
    __value_helpers_handlers.delete = user_handler_delete;
    __value_helpers_handlers.copy   = user_handler_copy;
    __value_helpers_handlers.move   = user_handler_move;
    __value_helpers_handlers.x      = &user_helpers;

    /* It's a work (#1) */
    do {
        storage_p storage = storage_creator();

        do {
            /* We don't use "move". We use "copy"*/
            __value_helpers_handlers.p_move = p_false;
            
            string data = new_string2(&s_func, "> ");
            storage->add(storage,
                         __value_helpers_handlers.x->new(data), NULL, NULL);
            del_string(data._self);
        } while(0);

        /* We don't use "copy". We use "move"*/
        __value_helpers_handlers.p_move = p_true;
        
        storage->add(storage,
                     __value_helpers_handlers.x->new(
                         new_string2(&s_func, "Hello")), NULL, NULL);
        storage->add(storage,
                     __value_helpers_handlers.x->new(
                         new_string2(&s_func, ",")), NULL, NULL);
        storage->add(storage,
                     __value_helpers_handlers.x->new(
                         new_string2(&s_func, " ")), NULL, NULL);
        storage->add(storage,
                     __value_helpers_handlers.x->new(
                         new_string2(&s_func, "World")), NULL, NULL);
        storage->add(storage,
                     __value_helpers_handlers.x->new(
                         new_string2(&s_func, "!")), NULL, NULL);
        
        void output1(value_t _value,
                     char const* _name,
                     char const* _comment) {
            (void) _name; (void) _comment;
            string s = __value_helpers_handlers.x->get(_value);
            (void) printf("%s", s.f->s_get(s._self));
        }

        storage->for_each(storage, output1);
        (void) puts("");
        
        storage_remover(&storage);
    } while(0);
    /* It's the end of work */

    bool p_true(void) {
        return true;
    }
    
    bool p_false(void) {
        return false;
    }
    
    return EXIT_SUCCESS;
}

/* ****************************************************************************
 * End of file
 **************************************************************************** */


build.sh:
#! /bin/bash

# To use:
# $ rm -f test; clear; ./build.sh; ./test
# $ rm -f test; clear; ./build.sh; valgrind ./test
# $ rm -f test; clear; ./build.sh; valgrind --leak-check=full ./test

gcc -std=gnu99 -Wall -Wextra storage.c test.c -lm -lc -o test
#gcc -std=gnu99 -Wall -Wextra -Wtrampolines storage.c test.c -lm -lc -o test


It's a result (without Valgrind):

bodro@vbodrov-pc:~/workspace/develop/test/test62$ rm -f test; ./build.sh; ./test
> Hello, World!
bodro@vbodrov-pc:~/workspace/develop/test/test62$

It's a result (with Valgrind):

bodro@vbodrov-pc:~/workspace/develop/test/test62$ rm -f test; ./build.sh; valgrind --leak-check=full ./test
==23326== Memcheck, a memory error detector
==23326== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==23326== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==23326== Command: ./test
==23326== 
> Hello, World!
==23326== 
==23326== HEAP SUMMARY:
==23326==     in use at exit: 0 bytes in 0 blocks
==23326==   total heap usage: 32 allocs, 32 frees, 1,594 bytes allocated
==23326== 
==23326== All heap blocks were freed -- no leaks are possible
==23326== 
==23326== For counts of detected and suppressed errors, rerun with: -v
==23326== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
bodro@vbodrov-pc:~/workspace/develop/test/test62$

Best regards,
Vasiliy V. Bodrov aka Bodro,
May 15-16, 2017.

How do you swap two variable?

Hi!

How do you swap two variable?

Look at this! It's a C++ code.
#include <iostream>

using namespace std;

inline void swap_var(int& _x, int& _y) {
    int _ = _x; _x = _y; _y = _;
}

inline void swap_xor(int& _x, int& _y) {
    _x ^= _y; _y ^= _x; _x ^= _y;
}

inline void swap_math(int& _x, int& _y) {
    _x += _y; _y = _x - _y; _x -= _y;
}

int main(void) {
    int a = 5;
    int b = 77201;

    cout << a << ":" << b << endl;
    swap_var(a, b);
    cout << a << ":" << b << endl;
    swap_xor(a, b);
    cout << a << ":" << b << endl;
    swap_math(a, b);
    cout << a << ":" << b << endl;
}


It's the result:
$ rm -f t; g++ -Wall -Wextra -Werror -O2 t.cpp -o t; ./t;
5:77201
77201:5
5:77201
77201:5
$


This is a C code.
#include <stdlib.h>
#include <stdio.h>

void swap_var(int* _x, int* _y) {
    int _ = *_x; *_x = *_y; *_y = _;
}

void swap_xor(int* _x, int* _y) {
    *_x ^= *_y; *_y ^= *_x; *_x ^= *_y;
}

void swap_math(int* _x, int* _y) {
    *_x += *_y; *_y = *_x - *_y; *_x -= *_y;
}

int main(void) {
    int a = 5;
    int b = 77201;

    (void) printf("%d:%d\n", a, b);
    swap_var(&a, &b);
    (void) printf("%d:%d\n", a, b);
    swap_xor(&a, &b);
    (void) printf("%d:%d\n", a, b);
    swap_math(&a, &b);
    (void) printf("%d:%d\n", a, b);

    return EXIT_SUCCESS;
}


It's the result:
$ rm -f t; gcc -Wall -Wextra -Werror -O2 t.c -o t; ./t
5:77201
77201:5
5:77201
77201:5
$

And finally, Erlang :)
-module(t).
-export([t/0]).

swap({X, Y}) ->
    {Y, X}.

t() ->
    A = {1, 2},
    swap(A).

It's the result:
$ erl
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-threads:10] [kernel-poll:false]

Eshell V7.3  (abort with ^G)
1> c(t).
{ok,t}
2> t:t().
{2,1}
3> q().
ok
4> $

Best regards,
Vasiliy V. Bodrov aka Bodro,
May 16, 2017.

Thursday, May 11, 2017

How to create a final class in C ++? Do you know?

Hi, my dear hackers!

We continue to talk about C++.
I assume you know the Java programming language. And also I assume you know keyword final. But do you think we can use it in C++? I mean the standard of the 98-th year (ISO/IEC 14882:1998). It turns out you can!

Look at this!


I'm trying to compile and run this code. I use GNU compiler and GNU/Linux as the operation system. I know and like them!

$ g++ -Wall -Wextra -Werror -o final final.cpp
$ ./final 
cons Lock
cons A
$ g++ -Wall -Wextra -Werror -o final final.cpp -DI_WANT_ERROR
final.cpp: In constructor 'B::B()':
final.cpp:13:2: error: 'Lock::Lock()' is private
  Lock() { cout << "cons Lock" << endl; }
  ^
final.cpp:26:6: error: within this context
  B() { cout << "cons B" << endl; }
      ^
$

You can see that we have an error when we use macro I_WANT_ERROR. Why?

The constructor of the class Lock cant't be called by the child classes. However class A is a friend of class Lock. So we can create an instance of class A because the constructor of class A can call the constructor of class Lock. Do you agree with me? I continue. But if we don't use a virtual inheritance we can create an instance of class B, because the constructor of class B can call the constructor of class A and the constructor of class A can call the constructor of class Lock. It's confusing and difficult, isn't it? But if you understand everything is simple.

Look at this: B -> A -> Lock.
It's OK? Do you understand me?

But if we use the virtual inheritance the constructor of class B must call the constructor of class Lock directly! But it can't do it because it doesn't have access to it (I mean the constructor of class Lock) because the constructor of class Lock is private and class B isn't a friend of class Lock. It's a very simple, my dear friend! And it's great!

This reaction as the final keywords applied to the class, isn't it? Do you agree with me? :)


Best regards,
Vasiliy V. Bodrov aka Bodro,
the 10-11-th of May, 2017 year.

Sunday, May 7, 2017

"A pure virtual method is called." Do you know how to do this in C++?

Hello, my dear friend!

"A pure virtual method is called". Do you know how to do this in C++?
 I'll show you this. I like GNU compiler, so I will use it.

Example #1. Simple.

Code:

#include <cstdlib>
#include <iostream>

class A {
public:
 A() {
  init();
 }

 void init(void) {
  f();
 }

 virtual void f(void) = 0;
};

class B : public A {
public:
 virtual void f(void) {
  std::cout << "f() in B" << std::endl;
 }
};

int main(void) {
 B b;
 b.f();
 
 return EXIT_SUCCESS;
}

Build and result:

bodro@bodronote:~/workspace/develop/test/pure_virtual_func$ export LC_ALL=C
bodro@bodronote:~/workspace/develop/test/pure_virtual_func$ ls -lah t1.cpp 
-rw-r--r-- 1 bodro bodro 297 May  7 13:24 t1.cpp
bodro@bodronote:~/workspace/develop/test/pure_virtual_func$ g++ -Wall -Wextra \
> -Werror -O0 -o t1.bin t1.cpp && ./t1.bin
pure virtual method called
terminate called without an active exception
Aborted
bodro@bodronote:~/workspace/develop/test/pure_virtual_func$

Example #2. I will use an assembler (AT&T).

Code:

#include <cstdlib>
#include <iostream>

class A {
public:
 A() {
  asm ("mov (%rax),%rcx");
  asm ("mov 0(%rcx),%rcx");
  asm ("call *%rcx");
 }

 virtual void f(void) = 0;
};

class B : public A {
public:
 virtual void f(void) {
  std::cout << "f() in B" << std::endl;
 }
};

int main(void) {
 B b;
 b.f();

 return EXIT_SUCCESS;
}

Build and result:

bodro@bodronote:~/workspace/develop/test/pure_virtual_func$ export LC_ALL=C
bodro@bodronote:~/workspace/develop/test/pure_virtual_func$ ls -lah t2.cpp 
-rw-r--r-- 1 bodro bodro 333 May  7 13:36 t2.cpp
bodro@bodronote:~/workspace/develop/test/pure_virtual_func$ g++ -Wall -Wextra \
> -Werror -O0 -o t2.bin t2.cpp && ./t1.bin 
pure virtual method called
terminate called without an active exception
Aborted
bodro@bodronote:~/workspace/develop/test/pure_virtual_func$

The story of how it works I'll write later.


Best regards,
Vasiliy V. Bodrov aka Bodro,
the 7-th of May, 2017 year.

How to reverse singly linked list in C (trivial way)

There is a beautiful morning. It's snowing. And I decided to write one more article. Today it will be just simple article where I would...