首页 数据库 mysql教程 Accessing static Data and Functions in Legacy C

Accessing static Data and Functions in Legacy C

Jun 07, 2016 pm 03:44 PM
and data static

http://www.renaissancesoftware.net/blog/archives/450 It’s a new year; last year was a leap year; so the quadrennial reports of leap year bugs are coming in. Apologies are in the press from Apple, TomTom, and Microsoft. Trains we stopped f

http://www.renaissancesoftware.net/blog/archives/450

It’s a new year; last year was a leap year; so the quadrennial reports of leap year bugs are coming in. Apologies are in the press from Apple, TomTom, and Microsoft. Trains we stopped from running in China. Somehow calling them glitches seems to make it someone else’s fault, something out of their control. How long have leap years been around? Julius Caesar introduced Leap Years in the Roman empire over 2000 years ago. The Gregorian calendar has been around since 1682. This is not a new idea, or a new bug.

I’m going to try to take one excuse away from the programmers that create these bugs by answering a question that comes up all the time, “How do I test static functions in my C code?”

In code developed using TDD, static functions are tested indirectly through the public interface. As I mentioned in a this article TDD is a code rot radar. It helps you see design problems. Needing direct access to hidden functions and data in C is a sign of code rot. It is time to refactor.

But what about existing legacy code that has statics? It is probably way past the time for idealism and time for some pragmatism. In this article and the next, we’ll look at how to get your code untouched into the test harness and access those pesky <span>static </span>variables and functions.

If you don’t mind touching your code, you could change all mentions of static to STATIC. Then using the preprocessor, STATIC can he set to static during production and to nothing for test, making the names globally accessible. In gcc you would use these command line options

  • For production builds use -dSTATIC=static
  • For unit test builds use -dSTATIC

Let’s look at two options that, at least for access to statics, you will not have to touch your code to get it under test. First is <span>#include</span>-ing your .c in the test file. In the next article we’ll build a test adapter .c that give access to the hidden parts.

We are going to revisit code that is similar to the original code responsible for the Zune Bug. I rewrote the code to avoid attracting any lawyers but it is structured similarly to the original Zune driver, complete with static data and functions that must be correct for the function to work.

The header file provides a data structure and initialization function for figuring out the information about the date. Here is the header:

typedef struct Date
{
    int daysSince1980;
    int year;
    int dayOfYear;
    int month;
    int dayOfMonth;
    int dayOfWeek;
} Date;
 
void Date_Init(Date *, int daysSince1980);
 
enum {
    SUN = 0, MON, TUE, WED, THU, FRI, SAT
};
 
enum {
    JAN = 0, FEB, MAR, APR, MAY, JUN,
    JUL, AUG, SEP, OCT, NOV, DEC
};
登录后复制

Date_Init populates the Date instance passed in. Ignoring the fact that I can probably fully test this by rigging the daysSince1980, and inspecting the contents of the resultingDate structure, we are going to see how we can directly test the hidden functions and data.

Date_Init has three hidden helper functions.

void Date_Init(Date* date, int daysSince1980)
{
     date->daysSince1980 = daysSince1980;
     FirstSetYearAndDayOfYear(date);
     ThenSetMonthAndDayOfMonth(date);
     FinallySetDayOfWeek(date);
}
登录后复制

Date_Init is the tip of the iceberg. All the interesting stuff is happening in the hidden data and functions:

#include "Date.h"
#include 
 
enum
{
    STARTING_YEAR = 1980, STARTING_WEEKDAY = TUE
};
 
static const int nonLeapYearDaysPerMonth[12] =
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 
static const int leapYearDaysPerMonth[12] =
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 
static bool IsLeapYear(int year)
{
    if (year % 400 == 0)
        return true;
    if (year % 100 == 0)
        return false;
    if (year % 4 == 0)
        return true;
    return false;
}
 
static int GetDaysInYear(int year)
{
    if (IsLeapYear(year))
        return 366;
    else
        return 365;
}
 
static void FirstSetYearAndDayOfYear(Date * date)
{
    int days = date->daysSince1980;
    int year = STARTING_YEAR;
    int daysInYear = GetDaysInYear(year);
 
    while (days > daysInYear)
    {
        year++;
        days -= daysInYear;
        daysInYear = GetDaysInYear(year);
    }
 
    date->dayOfYear = days;
    date->year = year;
}
 
static void ThenSetMonthAndDayOfMonth(Date * date)
{
    int month = 0;
    int days = date->dayOfYear;
    const int * daysPerMonth = nonLeapYearDaysPerMonth;
    if (IsLeapYear(date->year))
        daysPerMonth = leapYearDaysPerMonth;
 
    while (days > daysPerMonth[month])
    {
        days -= daysPerMonth[month];
        month++;
    }
    date->month = month + 1;
    date->dayOfMonth = days;
}
 
static void FinallySetDayOfWeek(Date * date)
{
     date->dayOfWeek =((date->daysSince1980-1)+STARTING_WEEKDAY)%7;
}
 
void Date_Init(Date* date, int daysSince1980)
{
     date->daysSince1980 = daysSince1980;
     FirstSetYearAndDayOfYear(date);
     ThenSetMonthAndDayOfMonth(date);
     FinallySetDayOfWeek(date);
}
登录后复制

Let’s say you want to check the days per month vectors. You might want to write a test to check the months against the handy poem we use here in the US: Thirty days, has September, April, June and November; all the rest have thirty-one, except for February. It has 28 except in leap year it has 29.

If you started by writing this test…

TEST(Date, sept_has_30_days)
{
    LONGS_EQUAL(30, nonLeapYearDaysPerMonth[SEP]);
}
登录后复制

… you get this error:

DateTest.cpp:41: error: 'nonLeapYearDaysPerMonth' was not declared in this scope
登录后复制

Let’s get access to the hidden statics in the test case by including Date.c instead ofDate.h in DateTest.cpp. The full test case file looks like this now:

#include "CppUTest/TestHarness.h"
 
extern "C"
{
#include "Date.c"
}
 
TEST_GROUP(Date)
{
};
 
TEST(Date, sept_has_30_days)
{
    LONGS_EQUAL(30, nonLeapYearDaysPerMonth[SEP]);
}
登录后复制

With a little refactoring days per month vectors can be checked like this:

#include "CppUTest/TestHarness.h"
 
extern "C"
{
#include "Date.c"
}
 
TEST_GROUP(Date)
{
    const int * daysPerYearVector;
 
    void setup()
    {
        daysPerYearVector = nonLeapYearDaysPerMonth;
    }
 
    void itsLeapYear()
    {
        daysPerYearVector = leapYearDaysPerMonth;
    }
 
    void CheckNumberOfDaysPerMonth(int month, int days)
    {
        LONGS_EQUAL(days, daysPerYearVector[month]);
    }
 
    void ThirtyDaysHasSeptEtc()
    {
        CheckNumberOfDaysPerMonth(SEP, 30);
        CheckNumberOfDaysPerMonth(APR, 30);
        CheckNumberOfDaysPerMonth(JUN, 30);
        CheckNumberOfDaysPerMonth(NOV, 30);
 
        CheckNumberOfDaysPerMonth(OCT, 31);
        CheckNumberOfDaysPerMonth(DEC, 31);
        CheckNumberOfDaysPerMonth(JAN, 31);
        CheckNumberOfDaysPerMonth(MAR, 31);
        CheckNumberOfDaysPerMonth(MAY, 31);
        CheckNumberOfDaysPerMonth(JUL, 31);
        CheckNumberOfDaysPerMonth(AUG, 31);
    }
 
    void ExceptFebruaryHas(int days)
    {
      CheckNumberOfDaysPerMonth(FEB, days);
    }
};
 
TEST(Date, non_leap_year_day_per_month_table)
{
    ThirtyDaysHasSeptEtc();
    ExceptFebruaryHas(28);
}
 
TEST(Date, leap_year_day_per_month_table)
{
    itsLeapYear();
    ThirtyDaysHasSeptEtc();
    ExceptFebruaryHas(28);
}
登录后复制

You have access to all the hidden stuff, so you can write the test for the static functions:

IsLeapYear()GetDaysInYear()FirstSetYearAndDayOfYear(),ThenSetMonthAndDayOfMonth(), and FinallySetDayOfWeek().

If Date been an abstract data type, with its data structure hidden in the .c file, the tests would all have access to structure members hidden from the rest of the world.

There is a downside to this approach, which probably does not matter in this case, but could in others. You can only have one test file that can include a given .c file. In the next article we’ll solve that problem.

Have you heard of any interesting leap year bugs? Did you prevent your own?

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1661
14
CakePHP 教程
1418
52
Laravel 教程
1311
25
PHP教程
1261
29
C# 教程
1234
24
c语言static的作用和用法是什么 c语言static的作用和用法是什么 Jan 31, 2024 pm 01:59 PM

c语言static的作用和用法:1、变量作用域;2、生命周期;3、函数内部;4、修饰全局变量;5、修饰函数;6、其他用途;详细介绍:1、变量作用域,当一个变量前有static关键字,那么这个变量的作用域被限制在声明它的文件内,也就是说,这个变量是“文件级作用域”,这对于防止变量的“重复定义”问题很有用;2、生命周期,静态变量在程序开始执行时初始化一次,并在程序结束时销毁等等。

data文件夹里面是什么数据 data文件夹里面是什么数据 May 05, 2023 pm 04:30 PM

data文件夹里面是系统及程序的数据,比如软件的设置和安装包等,Data文件夹中各个文件夹则代表的是不同类型的数据存放文件夹,无论Data文件指的是文件名Data还是扩展名data,都是系统或程序自定义的数据文件,Data是数据保存的备份类文件,一般可以用meidaplayer、记事本或word打开。

C语言中static关键字的实际应用场景及使用技巧 C语言中static关键字的实际应用场景及使用技巧 Feb 21, 2024 pm 07:21 PM

C语言中static关键字的实际应用场景及使用技巧一、概述static是C语言中的一个关键字,用于修饰变量和函数。它的作用是改变其在程序运行过程中的生命周期和可见性,使得变量和函数具有静态的特性。本文将介绍static关键字的实际应用场景及使用技巧,并通过具体的代码示例进行说明。二、静态变量延长变量的生命周期使用static关键字修饰局部变量可以将其生命周期

Java中的static、this、super、final怎么使用 Java中的static、this、super、final怎么使用 Apr 18, 2023 pm 03:40 PM

一、static  请先看下面这段程序:publicclassHello{publicstaticvoidmain(String[]args){//(1)System.out.println("Hello,world!");//(2)}}看过这段程序,对于大多数学过Java的从来说,都不陌生。即使没有学过Java,而学过其它的高级语言,例如C,那你也应该能看懂这段代码的意思。它只是简单的输出“Hello,world”,一点别的用处都没有,然而,它却展示了static关键字的主

mysql load data乱码怎么办 mysql load data乱码怎么办 Feb 16, 2023 am 10:37 AM

mysql load data乱码的解决办法:1、找到出现乱码的SQL语句;2、修改语句为“LOAD DATA LOCAL INFILE "employee.txt" INTO TABLE EMPLOYEE character set utf8;”即可。

Java修饰符abstract、static和final怎么用 Java修饰符abstract、static和final怎么用 Apr 26, 2023 am 09:46 AM

修饰符abstract(抽象的)一、abstract可以修饰类(1)被abstract修饰的类称为抽象类(2)语法:abstractclass类名{}(3)特点:抽象类不能单独创建对象,但是可以声明引用抽象类类名引用名;(4)抽象类可以定义成员变量和成员方法(5)抽象类有构造方法,用于创建子类对象时,jvm默认创建一个父类对象;抽象的构造方法应用在jvm创建父类对象时应用。二、abstract可以修饰方法(1)被asbtract修饰的方法被称为抽象方法(2)语法:访问修饰符abstract返回值

xdata和data有哪些区别 xdata和data有哪些区别 Dec 11, 2023 am 11:30 AM

区别有:1、xdata通常指的是自变量,data则是指整个数据集;2、xdata主要用于建立数据分析模型,data则是用于进行数据分析和统计;3、xdata通常用于回归分析、方差分析、预测建模,data则可以使用各种统计方法进行分析;4、xdata通常需要进行数据预处理,data则可以包含完整的原始数据。

static的作用 static的作用 Jan 24, 2024 pm 04:08 PM

static的作用:1、变量;2、方法;3、类;4、其他用途;5、多线程环境;6、性能优化;7、单例模式;8、常量;9、局部变量;10、内存布局优化;11、避免重复初始化;12、在函数中使用。详细介绍:1、变量,静态变量,当一个变量被声明为static时,它属于类级别,而不是实例级别,这意味着无论创建多少个对象,都只有一个静态变量存在,所有对象都共享这个静态变量等等。

See all articles